@foodpilot/foods 2.8.0 → 2.9.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/components/Chart/BreakdownTable/BreakdownTable.d.ts +35 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTable.js +137 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableEmpty.d.ts +14 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableEmpty.js +89 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableHeader.d.ts +14 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableHeader.js +85 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableRow.d.ts +16 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableRow.js +90 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableSkeleton.d.ts +3 -0
- package/dist/components/Chart/BreakdownTable/BreakdownTableSkeleton.js +58 -0
- package/dist/components/Chart/BreakdownTable/useBreakdownSorting.d.ts +19 -0
- package/dist/components/Chart/BreakdownTable/useBreakdownSorting.js +40 -0
- package/dist/components/Chart/BreakdownTable/useResizableRightColumn.d.ts +10 -0
- package/dist/components/Chart/BreakdownTable/useResizableRightColumn.js +42 -0
- package/dist/components/Chart/PolymorphicChart/charts/PolymorphicCumulativeChart.js +1 -0
- package/dist/components/Chart/index.d.ts +1 -0
- package/dist/main.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
export type BreakdownNode = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: number;
|
|
5
|
+
children?: BreakdownNode[];
|
|
6
|
+
};
|
|
7
|
+
export type BreakdownTableTextOptions = {
|
|
8
|
+
categoryHeader?: string;
|
|
9
|
+
scoreHeader?: string;
|
|
10
|
+
total?: string;
|
|
11
|
+
};
|
|
12
|
+
export type BreakdownTableProps = {
|
|
13
|
+
groups: BreakdownNode[];
|
|
14
|
+
valuesFormatterFn?: (value: number, precision?: number) => string;
|
|
15
|
+
valuesUnit?: string;
|
|
16
|
+
isLoading?: boolean;
|
|
17
|
+
headerLeft?: ReactNode;
|
|
18
|
+
headerRight?: ReactNode;
|
|
19
|
+
textOptions?: BreakdownTableTextOptions;
|
|
20
|
+
enableSorting?: boolean;
|
|
21
|
+
manualSort?: boolean;
|
|
22
|
+
sorting?: {
|
|
23
|
+
column: "label" | "value" | null;
|
|
24
|
+
direction: "asc" | "desc";
|
|
25
|
+
};
|
|
26
|
+
onSortingChange?: (s: {
|
|
27
|
+
column: "label" | "value" | null;
|
|
28
|
+
direction: "asc" | "desc";
|
|
29
|
+
}) => void;
|
|
30
|
+
resizable?: boolean;
|
|
31
|
+
initialRightColWidth?: number;
|
|
32
|
+
minRightColWidth?: number;
|
|
33
|
+
maxRightColWidth?: number;
|
|
34
|
+
};
|
|
35
|
+
export declare const BreakdownTable: (props: BreakdownTableProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useMemo } from "react";
|
|
3
|
+
import { useTheme, Box, TableContainer, Table, TableBody, TableRow, TableCell, Typography, Stack } from "@mui/material";
|
|
4
|
+
import { BreakdownTableRow } from "./BreakdownTableRow.js";
|
|
5
|
+
import { useBreakdownSorting } from "./useBreakdownSorting.js";
|
|
6
|
+
import { useResizableRightColumn } from "./useResizableRightColumn.js";
|
|
7
|
+
import { BreakdownTableSkeleton } from "./BreakdownTableSkeleton.js";
|
|
8
|
+
import { BreakdownTableEmpty } from "./BreakdownTableEmpty.js";
|
|
9
|
+
import { BreakdownTableHeader } from "./BreakdownTableHeader.js";
|
|
10
|
+
const BreakdownTable = (props) => {
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
const {
|
|
13
|
+
groups,
|
|
14
|
+
valuesFormatterFn = (v, p) => theme.numbers.valueFormatterFn(v, p),
|
|
15
|
+
valuesUnit,
|
|
16
|
+
isLoading,
|
|
17
|
+
textOptions
|
|
18
|
+
} = props;
|
|
19
|
+
const texts = { ...textOptions ?? {} };
|
|
20
|
+
const [expanded, setExpanded] = useState({});
|
|
21
|
+
const { sortingState, sortingEnabled, groupsToRender, toggleSort } = useBreakdownSorting({
|
|
22
|
+
groups,
|
|
23
|
+
sorting: props.sorting,
|
|
24
|
+
enableSorting: props.enableSorting,
|
|
25
|
+
manualSort: props.manualSort,
|
|
26
|
+
onSortingChange: props.onSortingChange
|
|
27
|
+
});
|
|
28
|
+
const { rightColWidth, onMouseDownResizer } = useResizableRightColumn({
|
|
29
|
+
resizable: props.resizable,
|
|
30
|
+
initialRightColWidth: props.initialRightColWidth,
|
|
31
|
+
minRightColWidth: props.minRightColWidth,
|
|
32
|
+
maxRightColWidth: props.maxRightColWidth
|
|
33
|
+
});
|
|
34
|
+
const total = useMemo(() => groups.reduce((acc, g) => acc + (Number(g.value) || 0), 0), [groups]);
|
|
35
|
+
const getRowCellSx = (level) => ({
|
|
36
|
+
borderBottom: level === 0 ? `1px solid ${theme.palette.divider}` : "none",
|
|
37
|
+
padding: theme.spacing(2, 3)
|
|
38
|
+
});
|
|
39
|
+
const indentForLevel = (level) => level > 0 ? theme.spacing(6 + (level - 1) * 4.5) : void 0;
|
|
40
|
+
if (isLoading) {
|
|
41
|
+
const rows = Math.max(5, (groups == null ? void 0 : groups.length) || 6);
|
|
42
|
+
return /* @__PURE__ */ jsx(BreakdownTableSkeleton, { rows });
|
|
43
|
+
}
|
|
44
|
+
const isEmpty = ((groups == null ? void 0 : groups.length) ?? 0) === 0;
|
|
45
|
+
if (isEmpty) {
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
BreakdownTableEmpty,
|
|
48
|
+
{
|
|
49
|
+
rightColWidth,
|
|
50
|
+
texts,
|
|
51
|
+
headerLeft: props.headerLeft,
|
|
52
|
+
headerRight: props.headerRight,
|
|
53
|
+
sortingEnabled,
|
|
54
|
+
sortingState,
|
|
55
|
+
onToggleSort: toggleSort,
|
|
56
|
+
resizable: props.resizable ?? true,
|
|
57
|
+
onMouseDownResizer
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
Box,
|
|
63
|
+
{
|
|
64
|
+
position: "relative",
|
|
65
|
+
width: "100%",
|
|
66
|
+
sx: {
|
|
67
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
68
|
+
borderRadius: theme.shape.borderRadius,
|
|
69
|
+
backgroundColor: theme.palette.background.paper
|
|
70
|
+
},
|
|
71
|
+
children: /* @__PURE__ */ jsx(TableContainer, { sx: { borderRadius: theme.shape.borderRadius }, children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs(TableBody, { children: [
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
BreakdownTableHeader,
|
|
74
|
+
{
|
|
75
|
+
rightColWidth,
|
|
76
|
+
sortingEnabled,
|
|
77
|
+
sortingState,
|
|
78
|
+
onToggleSort: toggleSort,
|
|
79
|
+
resizable: props.resizable ?? true,
|
|
80
|
+
onMouseDownResizer,
|
|
81
|
+
headerLeft: props.headerLeft,
|
|
82
|
+
headerRight: props.headerRight,
|
|
83
|
+
texts
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
groupsToRender.map((g) => /* @__PURE__ */ jsx(
|
|
87
|
+
BreakdownTableRow,
|
|
88
|
+
{
|
|
89
|
+
node: g,
|
|
90
|
+
level: 0,
|
|
91
|
+
path: g.label,
|
|
92
|
+
valuesFormatterFn,
|
|
93
|
+
valuesUnit,
|
|
94
|
+
expanded,
|
|
95
|
+
setExpanded,
|
|
96
|
+
getRowCellSx,
|
|
97
|
+
indentForLevel
|
|
98
|
+
},
|
|
99
|
+
`row-${g.label}`
|
|
100
|
+
)),
|
|
101
|
+
/* @__PURE__ */ jsxs(TableRow, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
TableCell,
|
|
104
|
+
{
|
|
105
|
+
sx: {
|
|
106
|
+
...getRowCellSx(0),
|
|
107
|
+
borderBottom: 0,
|
|
108
|
+
borderRight: `1px solid ${theme.palette.divider}`
|
|
109
|
+
},
|
|
110
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 700, color: theme.palette.text.primary }, children: texts.total })
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
/* @__PURE__ */ jsx(
|
|
114
|
+
TableCell,
|
|
115
|
+
{
|
|
116
|
+
sx: {
|
|
117
|
+
...getRowCellSx(0),
|
|
118
|
+
borderBottom: 0,
|
|
119
|
+
width: `${rightColWidth}px`,
|
|
120
|
+
minWidth: `${rightColWidth}px`,
|
|
121
|
+
maxWidth: `${rightColWidth}px`
|
|
122
|
+
},
|
|
123
|
+
align: "right",
|
|
124
|
+
children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", justifyContent: "flex-end", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontWeight: 700 }, children: valuesFormatterFn(total) }),
|
|
126
|
+
valuesUnit && /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: theme.palette.text.secondary }, children: valuesUnit })
|
|
127
|
+
] })
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
] })
|
|
131
|
+
] }) }) })
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
export {
|
|
136
|
+
BreakdownTable
|
|
137
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BreakdownTableTextOptions } from './BreakdownTable';
|
|
2
|
+
import { SortingState } from './useBreakdownSorting';
|
|
3
|
+
export type BreakdownTableEmptyProps = {
|
|
4
|
+
rightColWidth: number;
|
|
5
|
+
texts: BreakdownTableTextOptions;
|
|
6
|
+
headerLeft?: React.ReactNode;
|
|
7
|
+
headerRight?: React.ReactNode;
|
|
8
|
+
sortingEnabled: boolean;
|
|
9
|
+
sortingState: SortingState;
|
|
10
|
+
onToggleSort: (column: "label" | "value") => void;
|
|
11
|
+
resizable?: boolean;
|
|
12
|
+
onMouseDownResizer: (e: React.MouseEvent) => void;
|
|
13
|
+
};
|
|
14
|
+
export declare const BreakdownTableEmpty: (props: BreakdownTableEmptyProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme, Box, TableContainer, Table, TableBody, TableRow, TableCell, Stack, Typography } from "@mui/material";
|
|
3
|
+
import { FoodsBadge } from "../../Badge/FoodsBadge.js";
|
|
4
|
+
import { BreakdownTableHeader } from "./BreakdownTableHeader.js";
|
|
5
|
+
const BreakdownTableEmpty = (props) => {
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
const {
|
|
8
|
+
rightColWidth,
|
|
9
|
+
texts,
|
|
10
|
+
headerLeft,
|
|
11
|
+
headerRight,
|
|
12
|
+
sortingEnabled,
|
|
13
|
+
sortingState,
|
|
14
|
+
onToggleSort,
|
|
15
|
+
resizable = true,
|
|
16
|
+
onMouseDownResizer
|
|
17
|
+
} = props;
|
|
18
|
+
return /* @__PURE__ */ jsx(
|
|
19
|
+
Box,
|
|
20
|
+
{
|
|
21
|
+
position: "relative",
|
|
22
|
+
width: "100%",
|
|
23
|
+
sx: {
|
|
24
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
25
|
+
borderRadius: theme.shape.borderRadius,
|
|
26
|
+
backgroundColor: theme.palette.background.paper
|
|
27
|
+
},
|
|
28
|
+
children: /* @__PURE__ */ jsx(TableContainer, { sx: { borderRadius: theme.shape.borderRadius }, children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs(TableBody, { children: [
|
|
29
|
+
/* @__PURE__ */ jsx(
|
|
30
|
+
BreakdownTableHeader,
|
|
31
|
+
{
|
|
32
|
+
rightColWidth,
|
|
33
|
+
sortingEnabled,
|
|
34
|
+
sortingState,
|
|
35
|
+
onToggleSort,
|
|
36
|
+
resizable,
|
|
37
|
+
onMouseDownResizer,
|
|
38
|
+
headerLeft,
|
|
39
|
+
headerRight,
|
|
40
|
+
texts
|
|
41
|
+
}
|
|
42
|
+
),
|
|
43
|
+
/* @__PURE__ */ jsxs(TableRow, { children: [
|
|
44
|
+
/* @__PURE__ */ jsx(
|
|
45
|
+
TableCell,
|
|
46
|
+
{
|
|
47
|
+
sx: {
|
|
48
|
+
padding: theme.spacing(2),
|
|
49
|
+
borderBottom: 0,
|
|
50
|
+
borderRight: `1px solid ${theme.palette.divider}`
|
|
51
|
+
},
|
|
52
|
+
children: /* @__PURE__ */ jsx(Stack, { direction: "row", alignItems: "center", spacing: 2, children: /* @__PURE__ */ jsx(
|
|
53
|
+
FoodsBadge,
|
|
54
|
+
{
|
|
55
|
+
size: 4,
|
|
56
|
+
icon: "empty",
|
|
57
|
+
boxProps: {
|
|
58
|
+
sx: {
|
|
59
|
+
borderRadius: "3px",
|
|
60
|
+
backgroundColor: theme.custom.grey[400],
|
|
61
|
+
color: theme.custom.grey[1400]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
) })
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ jsx(
|
|
69
|
+
TableCell,
|
|
70
|
+
{
|
|
71
|
+
sx: {
|
|
72
|
+
padding: theme.spacing(2),
|
|
73
|
+
borderBottom: 0,
|
|
74
|
+
width: `${rightColWidth}px`,
|
|
75
|
+
minWidth: `${rightColWidth}px`,
|
|
76
|
+
maxWidth: `${rightColWidth}px`
|
|
77
|
+
},
|
|
78
|
+
align: "right",
|
|
79
|
+
children: /* @__PURE__ */ jsx(Typography, { component: "span", children: "—" })
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
] })
|
|
83
|
+
] }) }) })
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
export {
|
|
88
|
+
BreakdownTableEmpty
|
|
89
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { BreakdownTableTextOptions } from './BreakdownTable';
|
|
2
|
+
import { SortingState } from './useBreakdownSorting';
|
|
3
|
+
export type BreakdownTableHeaderProps = {
|
|
4
|
+
rightColWidth: number;
|
|
5
|
+
sortingEnabled: boolean;
|
|
6
|
+
sortingState: SortingState;
|
|
7
|
+
onToggleSort: (column: "label" | "value") => void;
|
|
8
|
+
resizable?: boolean;
|
|
9
|
+
onMouseDownResizer: (e: React.MouseEvent) => void;
|
|
10
|
+
headerLeft?: React.ReactNode;
|
|
11
|
+
headerRight?: React.ReactNode;
|
|
12
|
+
texts: BreakdownTableTextOptions;
|
|
13
|
+
};
|
|
14
|
+
export declare const BreakdownTableHeader: (props: BreakdownTableHeaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme, TableRow, TableCell, Stack, Typography, Box } from "@mui/material";
|
|
3
|
+
import { FoodsIcon } from "../../Icons/FoodsIcon.js";
|
|
4
|
+
const BreakdownTableHeader = (props) => {
|
|
5
|
+
const theme = useTheme();
|
|
6
|
+
const {
|
|
7
|
+
rightColWidth,
|
|
8
|
+
sortingEnabled,
|
|
9
|
+
sortingState,
|
|
10
|
+
onToggleSort,
|
|
11
|
+
resizable = true,
|
|
12
|
+
onMouseDownResizer,
|
|
13
|
+
headerLeft,
|
|
14
|
+
headerRight,
|
|
15
|
+
texts
|
|
16
|
+
} = props;
|
|
17
|
+
const headerCellSx = {
|
|
18
|
+
backgroundColor: theme.palette.grey[200],
|
|
19
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
20
|
+
py: theme.spacing(3),
|
|
21
|
+
px: theme.spacing(3),
|
|
22
|
+
color: theme.palette.text.primary
|
|
23
|
+
};
|
|
24
|
+
return /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { ...headerCellSx, borderRight: `1px solid ${theme.palette.divider}` }, children: headerLeft ?? /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "space-between", children: [
|
|
26
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 500, color: theme.palette.text.primary }, children: texts.categoryHeader }),
|
|
27
|
+
sortingEnabled && /* @__PURE__ */ jsx(
|
|
28
|
+
Box,
|
|
29
|
+
{
|
|
30
|
+
onClick: () => onToggleSort("label"),
|
|
31
|
+
sx: { display: "flex", alignItems: "center", cursor: "pointer", p: theme.spacing(0.5) },
|
|
32
|
+
children: /* @__PURE__ */ jsx(
|
|
33
|
+
FoodsIcon,
|
|
34
|
+
{
|
|
35
|
+
size: 2,
|
|
36
|
+
icon: sortingState.column === "label" ? sortingState.direction === "asc" ? "sortAscending" : "sortDescending" : "sortNoSorting"
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
] }) }),
|
|
42
|
+
/* @__PURE__ */ jsxs(
|
|
43
|
+
TableCell,
|
|
44
|
+
{
|
|
45
|
+
sx: {
|
|
46
|
+
...headerCellSx,
|
|
47
|
+
width: `${rightColWidth}px`,
|
|
48
|
+
minWidth: `${rightColWidth}px`,
|
|
49
|
+
maxWidth: `${rightColWidth}px`,
|
|
50
|
+
position: "relative"
|
|
51
|
+
},
|
|
52
|
+
align: "right",
|
|
53
|
+
children: [
|
|
54
|
+
headerRight ?? /* @__PURE__ */ jsxs(Stack, { direction: "row", alignItems: "center", justifyContent: "flex-end", spacing: 1, children: [
|
|
55
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 500, color: theme.palette.text.primary }, children: texts.scoreHeader }),
|
|
56
|
+
sortingEnabled && /* @__PURE__ */ jsx(
|
|
57
|
+
Box,
|
|
58
|
+
{
|
|
59
|
+
onClick: () => onToggleSort("value"),
|
|
60
|
+
sx: { display: "flex", alignItems: "center", cursor: "pointer", p: theme.spacing(0.5) },
|
|
61
|
+
children: /* @__PURE__ */ jsx(
|
|
62
|
+
FoodsIcon,
|
|
63
|
+
{
|
|
64
|
+
size: 2,
|
|
65
|
+
icon: sortingState.column === "value" ? sortingState.direction === "asc" ? "sortAscending" : "sortDescending" : "sortNoSorting"
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] }),
|
|
71
|
+
resizable && /* @__PURE__ */ jsx(
|
|
72
|
+
Box,
|
|
73
|
+
{
|
|
74
|
+
onMouseDown: onMouseDownResizer,
|
|
75
|
+
sx: { position: "absolute", left: -4, top: 0, bottom: 0, width: 8, cursor: "col-resize" }
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] });
|
|
82
|
+
};
|
|
83
|
+
export {
|
|
84
|
+
BreakdownTableHeader
|
|
85
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Theme } from '@mui/material';
|
|
2
|
+
import { SystemStyleObject } from '@mui/system';
|
|
3
|
+
import { BreakdownNode } from './BreakdownTable';
|
|
4
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
5
|
+
export type BreakdownTableRowProps = {
|
|
6
|
+
node: BreakdownNode;
|
|
7
|
+
level: number;
|
|
8
|
+
path: string;
|
|
9
|
+
valuesFormatterFn: (value: number, precision?: number) => string;
|
|
10
|
+
valuesUnit?: string;
|
|
11
|
+
expanded: Record<string, boolean>;
|
|
12
|
+
setExpanded: Dispatch<SetStateAction<Record<string, boolean>>>;
|
|
13
|
+
getRowCellSx: (level: number) => SystemStyleObject<Theme>;
|
|
14
|
+
indentForLevel: (level: number) => string | undefined;
|
|
15
|
+
};
|
|
16
|
+
export declare const BreakdownTableRow: (props: BreakdownTableRowProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme, TableRow, TableCell, Stack, Box, Typography } from "@mui/material";
|
|
3
|
+
import { FoodsIcon } from "../../Icons/FoodsIcon.js";
|
|
4
|
+
const BreakdownTableRow = (props) => {
|
|
5
|
+
var _a;
|
|
6
|
+
const theme = useTheme();
|
|
7
|
+
const { node, level, path, valuesFormatterFn, valuesUnit, expanded, setExpanded, getRowCellSx, indentForLevel } = props;
|
|
8
|
+
const isExpanded = expanded[path] === true;
|
|
9
|
+
const hasChildren = !!((_a = node.children) == null ? void 0 : _a.length);
|
|
10
|
+
const borderBottomStyle = level === 0 && !isExpanded ? `1px solid ${theme.palette.divider}` : "none";
|
|
11
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12
|
+
/* @__PURE__ */ jsxs(TableRow, { children: [
|
|
13
|
+
/* @__PURE__ */ jsx(
|
|
14
|
+
TableCell,
|
|
15
|
+
{
|
|
16
|
+
sx: {
|
|
17
|
+
...getRowCellSx(level),
|
|
18
|
+
borderBottom: borderBottomStyle,
|
|
19
|
+
borderRight: `1px solid ${theme.palette.divider}`,
|
|
20
|
+
pl: indentForLevel(level)
|
|
21
|
+
},
|
|
22
|
+
children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 1.5, alignItems: "center", children: [
|
|
23
|
+
hasChildren ? /* @__PURE__ */ jsx(
|
|
24
|
+
Box,
|
|
25
|
+
{
|
|
26
|
+
component: "button",
|
|
27
|
+
"aria-label": isExpanded ? "collapse" : "expand",
|
|
28
|
+
onClick: () => setExpanded((e) => ({ ...e, [path]: !isExpanded })),
|
|
29
|
+
sx: {
|
|
30
|
+
width: theme.spacing(3),
|
|
31
|
+
height: theme.spacing(3),
|
|
32
|
+
display: "flex",
|
|
33
|
+
alignItems: "center",
|
|
34
|
+
justifyContent: "center",
|
|
35
|
+
borderRadius: theme.spacing(3.75),
|
|
36
|
+
border: `1px solid ${theme.palette.grey[300]}`,
|
|
37
|
+
backgroundColor: theme.palette.background.paper,
|
|
38
|
+
cursor: "pointer",
|
|
39
|
+
padding: 0,
|
|
40
|
+
outline: "none",
|
|
41
|
+
borderColor: theme.palette.grey[300],
|
|
42
|
+
"&:focus-visible": {
|
|
43
|
+
boxShadow: `0 0 0 2px ${theme.palette.primary.light}`
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
children: /* @__PURE__ */ jsx(FoodsIcon, { size: 2, icon: isExpanded ? "arrowUpShort" : "arrowRightShort" })
|
|
47
|
+
}
|
|
48
|
+
) : /* @__PURE__ */ jsx(Fragment, {}),
|
|
49
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { fontWeight: 700, color: theme.palette.text.primary }, children: node.label })
|
|
50
|
+
] })
|
|
51
|
+
}
|
|
52
|
+
),
|
|
53
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { ...getRowCellSx(level), borderBottom: borderBottomStyle }, align: "right", children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 0.5, alignItems: "center", justifyContent: "flex-end", children: [
|
|
54
|
+
/* @__PURE__ */ jsx(Typography, { sx: { fontWeight: 700 }, children: valuesFormatterFn(node.value) }),
|
|
55
|
+
valuesUnit && /* @__PURE__ */ jsx(Typography, { variant: "body2", sx: { color: theme.palette.text.secondary }, children: valuesUnit })
|
|
56
|
+
] }) })
|
|
57
|
+
] }, `row-${path}`),
|
|
58
|
+
hasChildren && isExpanded && node.children.map((child) => /* @__PURE__ */ jsx(
|
|
59
|
+
BreakdownTableRow,
|
|
60
|
+
{
|
|
61
|
+
node: child,
|
|
62
|
+
level: level + 1,
|
|
63
|
+
path: `${path} > ${child.label}`,
|
|
64
|
+
valuesFormatterFn,
|
|
65
|
+
valuesUnit,
|
|
66
|
+
expanded,
|
|
67
|
+
setExpanded,
|
|
68
|
+
getRowCellSx,
|
|
69
|
+
indentForLevel
|
|
70
|
+
},
|
|
71
|
+
`row-${path} > ${child.label}`
|
|
72
|
+
)),
|
|
73
|
+
level === 0 && hasChildren && isExpanded && /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
74
|
+
/* @__PURE__ */ jsx(
|
|
75
|
+
TableCell,
|
|
76
|
+
{
|
|
77
|
+
sx: {
|
|
78
|
+
padding: 0,
|
|
79
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
80
|
+
borderRight: `1px solid ${theme.palette.divider}`
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { padding: 0, borderBottom: `1px solid ${theme.palette.divider}` } })
|
|
85
|
+
] }, `separator-${path}`)
|
|
86
|
+
] });
|
|
87
|
+
};
|
|
88
|
+
export {
|
|
89
|
+
BreakdownTableRow
|
|
90
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTheme, Box, TableContainer, Table, TableBody, TableRow, TableCell, Skeleton, Stack } from "@mui/material";
|
|
3
|
+
const BreakdownTableSkeleton = ({ rows }) => {
|
|
4
|
+
const theme = useTheme();
|
|
5
|
+
const headerCellSx = {
|
|
6
|
+
backgroundColor: theme.palette.grey[200],
|
|
7
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
8
|
+
py: theme.spacing(3),
|
|
9
|
+
px: theme.spacing(3),
|
|
10
|
+
color: theme.palette.text.primary
|
|
11
|
+
};
|
|
12
|
+
const getRowCellSx = {
|
|
13
|
+
borderBottom: `1px solid ${theme.palette.divider}`,
|
|
14
|
+
padding: theme.spacing(2, 3)
|
|
15
|
+
};
|
|
16
|
+
return /* @__PURE__ */ jsx(
|
|
17
|
+
Box,
|
|
18
|
+
{
|
|
19
|
+
position: "relative",
|
|
20
|
+
width: "100%",
|
|
21
|
+
sx: {
|
|
22
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
23
|
+
borderRadius: theme.shape.borderRadius,
|
|
24
|
+
backgroundColor: theme.palette.background.paper
|
|
25
|
+
},
|
|
26
|
+
children: /* @__PURE__ */ jsx(TableContainer, { sx: { borderRadius: theme.shape.borderRadius }, children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsxs(TableBody, { children: [
|
|
27
|
+
/* @__PURE__ */ jsxs(TableRow, { children: [
|
|
28
|
+
/* @__PURE__ */ jsx(
|
|
29
|
+
TableCell,
|
|
30
|
+
{
|
|
31
|
+
sx: { ...headerCellSx, borderRight: `1px solid ${theme.palette.divider}` },
|
|
32
|
+
width: "auto",
|
|
33
|
+
children: /* @__PURE__ */ jsx(Skeleton, { width: 120, height: 18 })
|
|
34
|
+
}
|
|
35
|
+
),
|
|
36
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { ...headerCellSx, width: theme.spacing(25) }, align: "right", children: /* @__PURE__ */ jsx(Skeleton, { width: 80, height: 18, sx: { ml: "auto" } }) })
|
|
37
|
+
] }),
|
|
38
|
+
Array.from({ length: rows }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
39
|
+
/* @__PURE__ */ jsx(TableCell, { sx: { ...getRowCellSx, borderRight: `1px solid ${theme.palette.divider}` }, children: /* @__PURE__ */ jsxs(Stack, { direction: "row", spacing: 2, alignItems: "center", children: [
|
|
40
|
+
/* @__PURE__ */ jsx(
|
|
41
|
+
Skeleton,
|
|
42
|
+
{
|
|
43
|
+
variant: "circular",
|
|
44
|
+
width: theme.spacing(3),
|
|
45
|
+
height: theme.spacing(3)
|
|
46
|
+
}
|
|
47
|
+
),
|
|
48
|
+
/* @__PURE__ */ jsx(Skeleton, { width: "60%", height: 18 })
|
|
49
|
+
] }) }),
|
|
50
|
+
/* @__PURE__ */ jsx(TableCell, { sx: getRowCellSx, align: "right", children: /* @__PURE__ */ jsx(Skeleton, { width: 100, height: 18, sx: { ml: "auto" } }) })
|
|
51
|
+
] }, `breakdown-table-skeleton-${i}`))
|
|
52
|
+
] }) }) })
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
export {
|
|
57
|
+
BreakdownTableSkeleton
|
|
58
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BreakdownNode } from './BreakdownTable';
|
|
2
|
+
export type SortingState = {
|
|
3
|
+
column: "label" | "value" | null;
|
|
4
|
+
direction: "asc" | "desc";
|
|
5
|
+
};
|
|
6
|
+
type Params = {
|
|
7
|
+
groups: BreakdownNode[];
|
|
8
|
+
sorting?: SortingState;
|
|
9
|
+
enableSorting?: boolean;
|
|
10
|
+
manualSort?: boolean;
|
|
11
|
+
onSortingChange?: (s: SortingState) => void;
|
|
12
|
+
};
|
|
13
|
+
export declare const useBreakdownSorting: ({ groups, sorting, enableSorting, manualSort, onSortingChange, }: Params) => {
|
|
14
|
+
readonly sortingState: SortingState;
|
|
15
|
+
readonly sortingEnabled: boolean;
|
|
16
|
+
readonly groupsToRender: BreakdownNode[];
|
|
17
|
+
readonly toggleSort: (column: "label" | "value") => void;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useState, useMemo } from "react";
|
|
2
|
+
const useBreakdownSorting = ({
|
|
3
|
+
groups,
|
|
4
|
+
sorting,
|
|
5
|
+
enableSorting = true,
|
|
6
|
+
manualSort = false,
|
|
7
|
+
onSortingChange
|
|
8
|
+
}) => {
|
|
9
|
+
const [internalSorting, setInternalSorting] = useState({ column: null, direction: "asc" });
|
|
10
|
+
const sortingState = sorting ?? internalSorting;
|
|
11
|
+
const sortingEnabled = enableSorting;
|
|
12
|
+
const setSorting = (s) => {
|
|
13
|
+
onSortingChange == null ? void 0 : onSortingChange(s);
|
|
14
|
+
setInternalSorting(s);
|
|
15
|
+
};
|
|
16
|
+
const toggleSort = (column) => {
|
|
17
|
+
if (sortingEnabled) {
|
|
18
|
+
const next = sortingState.column !== column ? { column, direction: "asc" } : { column, direction: sortingState.direction === "asc" ? "desc" : "asc" };
|
|
19
|
+
setSorting(next);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
const sortNodes = (nodes) => {
|
|
23
|
+
if (sortingState.column) {
|
|
24
|
+
const arr = [...nodes].sort((a, b) => {
|
|
25
|
+
if (sortingState.column === "label") return a.label.localeCompare(b.label);
|
|
26
|
+
return (a.value || 0) - (b.value || 0);
|
|
27
|
+
});
|
|
28
|
+
if (sortingState.direction === "desc") arr.reverse();
|
|
29
|
+
return arr.map((n) => ({ ...n, children: n.children ? sortNodes(n.children) : void 0 }));
|
|
30
|
+
}
|
|
31
|
+
return nodes;
|
|
32
|
+
};
|
|
33
|
+
const groupsToRender = useMemo(() => {
|
|
34
|
+
return !manualSort && sortingState.column ? sortNodes(groups) : groups;
|
|
35
|
+
}, [groups, manualSort, sortingState]);
|
|
36
|
+
return { sortingState, sortingEnabled, groupsToRender, toggleSort };
|
|
37
|
+
};
|
|
38
|
+
export {
|
|
39
|
+
useBreakdownSorting
|
|
40
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ResizableParams = {
|
|
2
|
+
resizable?: boolean;
|
|
3
|
+
initialRightColWidth?: number;
|
|
4
|
+
minRightColWidth?: number;
|
|
5
|
+
maxRightColWidth?: number;
|
|
6
|
+
};
|
|
7
|
+
export declare const useResizableRightColumn: ({ resizable, initialRightColWidth, minRightColWidth, maxRightColWidth, }: ResizableParams) => {
|
|
8
|
+
readonly rightColWidth: number;
|
|
9
|
+
readonly onMouseDownResizer: (e: React.MouseEvent) => void;
|
|
10
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useState, useRef } from "react";
|
|
2
|
+
import { useTheme } from "@mui/material";
|
|
3
|
+
const useResizableRightColumn = ({
|
|
4
|
+
resizable = true,
|
|
5
|
+
initialRightColWidth,
|
|
6
|
+
minRightColWidth,
|
|
7
|
+
maxRightColWidth
|
|
8
|
+
}) => {
|
|
9
|
+
const theme = useTheme();
|
|
10
|
+
const defaultRightWidth = Number.parseInt(String(theme.spacing(25)).replace("px", "")) || 200;
|
|
11
|
+
const minW = minRightColWidth ?? 200;
|
|
12
|
+
const maxW = maxRightColWidth ?? 460;
|
|
13
|
+
const [rightColWidth, setRightColWidth] = useState(initialRightColWidth ?? defaultRightWidth);
|
|
14
|
+
const isResizing = useRef(false);
|
|
15
|
+
const startX = useRef(0);
|
|
16
|
+
const startWidth = useRef(0);
|
|
17
|
+
const onMouseDownResizer = (e) => {
|
|
18
|
+
if (!resizable) return;
|
|
19
|
+
isResizing.current = true;
|
|
20
|
+
startX.current = e.clientX;
|
|
21
|
+
startWidth.current = rightColWidth;
|
|
22
|
+
const handleMove = (ev) => {
|
|
23
|
+
if (!isResizing.current) return;
|
|
24
|
+
const dx = ev.clientX - startX.current;
|
|
25
|
+
const next = Math.max(minW, Math.min(maxW, startWidth.current - dx));
|
|
26
|
+
setRightColWidth(next);
|
|
27
|
+
};
|
|
28
|
+
const handleUp = () => {
|
|
29
|
+
if (!isResizing.current) return;
|
|
30
|
+
isResizing.current = false;
|
|
31
|
+
document.removeEventListener("mousemove", handleMove);
|
|
32
|
+
document.removeEventListener("mouseup", handleUp);
|
|
33
|
+
};
|
|
34
|
+
document.addEventListener("mousemove", handleMove);
|
|
35
|
+
document.addEventListener("mouseup", handleUp);
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
};
|
|
38
|
+
return { rightColWidth, onMouseDownResizer };
|
|
39
|
+
};
|
|
40
|
+
export {
|
|
41
|
+
useResizableRightColumn
|
|
42
|
+
};
|
package/dist/main.js
CHANGED
|
@@ -43,6 +43,7 @@ import { CustomizedAxisTick } from "./components/Chart/HistogramComparisonChart/
|
|
|
43
43
|
import { HistogramComparisonChart } from "./components/Chart/HistogramComparisonChart/HistogramComparisonChart.js";
|
|
44
44
|
import { PolymorphicChart } from "./components/Chart/PolymorphicChart/PolymorphicChart.js";
|
|
45
45
|
import { PolymorphicChartRenderer } from "./components/Chart/PolymorphicChart/PolymorphicChartRenderer.js";
|
|
46
|
+
import { BreakdownTable } from "./components/Chart/BreakdownTable/BreakdownTable.js";
|
|
46
47
|
import { PrimaryCheckbox } from "./components/Checkbox/PrimaryCheckbox.js";
|
|
47
48
|
import { anyToCheckbox, checkboxToAny } from "./components/Checkbox/helperFunctions.js";
|
|
48
49
|
import { FoodsCheckbox } from "./components/Checkbox/FoodsCheckbox.js";
|
|
@@ -157,6 +158,7 @@ export {
|
|
|
157
158
|
BlockListContainer,
|
|
158
159
|
BottomSheetDialog,
|
|
159
160
|
BoxStyled,
|
|
161
|
+
BreakdownTable,
|
|
160
162
|
BredTheme,
|
|
161
163
|
BredThemeOptions,
|
|
162
164
|
Button,
|