@thebuoyant-tsdev/mui-ts-library 3.14.1 → 3.16.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/README.de.md +62 -0
- package/README.md +62 -0
- package/dist/components/gantt-chart/GanttChart.types.d.ts +2 -0
- package/dist/components/gantt-chart/GanttChart.types.js +1 -0
- package/dist/components/gantt-chart/GanttTaskDialog.js +170 -124
- package/dist/components/radial-stacked-bar-chart/RadialStackedBarChart.d.ts +5 -0
- package/dist/components/radial-stacked-bar-chart/RadialStackedBarChart.js +285 -0
- package/dist/components/radial-stacked-bar-chart/RadialStackedBarChart.types.d.ts +91 -0
- package/dist/components/radial-stacked-bar-chart/RadialStackedBarChart.types.js +4 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/package.json +2 -2
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { DEFAULT_RADIAL_STACKED_BAR_CHART_TRANSLATION as e } from "./RadialStackedBarChart.types.js";
|
|
2
|
+
import { useCallback as t, useLayoutEffect as n, useMemo as r, useRef as i, useState as a } from "react";
|
|
3
|
+
import { Box as o, Tooltip as s, Typography as c, useTheme as l } from "@mui/material";
|
|
4
|
+
import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
5
|
+
import * as p from "d3";
|
|
6
|
+
//#region src/components/radial-stacked-bar-chart/RadialStackedBarChart.tsx
|
|
7
|
+
function m(e, t = 0, n = ".", r = ",") {
|
|
8
|
+
let [i, a] = e.toFixed(Math.max(0, t)).split("."), o = i.replace(/\B(?=(\d{3})+(?!\d))/g, r);
|
|
9
|
+
return a ? `${o}${n}${a}` : o;
|
|
10
|
+
}
|
|
11
|
+
function ee(e) {
|
|
12
|
+
return Math.abs(e) >= 1e9 ? `${(e / 1e9).toFixed(1).replace(/\.0$/, "")}B` : Math.abs(e) >= 1e6 ? `${(e / 1e6).toFixed(1).replace(/\.0$/, "")}M` : Math.abs(e) >= 1e3 ? `${(e / 1e3).toFixed(1).replace(/\.0$/, "")}k` : String(e);
|
|
13
|
+
}
|
|
14
|
+
function te(e) {
|
|
15
|
+
return e.map((e) => typeof e == "string" ? {
|
|
16
|
+
key: e,
|
|
17
|
+
label: e
|
|
18
|
+
} : e);
|
|
19
|
+
}
|
|
20
|
+
function ne({ barData: e, seriesKey: t, seriesLabel: n, decimals: r, decimalSep: i, thousandSep: a }) {
|
|
21
|
+
let s = e.values[t] ?? 0, l = p.sum(Object.values(e.values)), u = l > 0 ? Math.round(s / l * 1e3) / 10 : 0;
|
|
22
|
+
return /* @__PURE__ */ f(o, {
|
|
23
|
+
sx: { py: .25 },
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ d(c, {
|
|
26
|
+
variant: "caption",
|
|
27
|
+
sx: {
|
|
28
|
+
fontWeight: "bold",
|
|
29
|
+
display: "block"
|
|
30
|
+
},
|
|
31
|
+
children: e.label
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ f(c, {
|
|
34
|
+
variant: "caption",
|
|
35
|
+
sx: {
|
|
36
|
+
display: "block",
|
|
37
|
+
opacity: .85
|
|
38
|
+
},
|
|
39
|
+
children: [
|
|
40
|
+
n,
|
|
41
|
+
": ",
|
|
42
|
+
m(s, r, i, a)
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
/* @__PURE__ */ f(c, {
|
|
46
|
+
variant: "caption",
|
|
47
|
+
sx: {
|
|
48
|
+
display: "block",
|
|
49
|
+
opacity: .65,
|
|
50
|
+
mt: .25
|
|
51
|
+
},
|
|
52
|
+
children: [
|
|
53
|
+
u,
|
|
54
|
+
"% · Total: ",
|
|
55
|
+
m(l, r, i, a)
|
|
56
|
+
]
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function h({ data: c, keys: m, size: h = 500, innerRadius: g, barPadding: _ = .12, chartColors: v, colorConfig: y, showLabels: b = !0, showGridLines: x = !0, gridLineCount: S = 3, showGridValues: re = !0, showLegend: C = !0, sortBy: w = "none", valueDecimalCount: ie = 0, valueDecimalSeparator: ae = ".", valueThousandsSeparator: oe = ",", gridValueFormatter: se, zoomable: T = !1, onBarClick: ce, disabled: E = !1, translation: D }) {
|
|
62
|
+
let O = l(), k = {
|
|
63
|
+
...e,
|
|
64
|
+
...D
|
|
65
|
+
}, A = r(() => te(m), [m]), j = r(() => A.map((e) => e.key), [A]), M = c.length === 0 || A.length === 0, le = b ? 63 : 16, N = h / 2 - le, P = g == null ? Math.round(h * .18) : Math.max(0, Math.min(g, N - 1)), ue = [
|
|
66
|
+
O.palette.primary.main,
|
|
67
|
+
O.palette.secondary.main,
|
|
68
|
+
O.palette.success.main,
|
|
69
|
+
O.palette.warning.main,
|
|
70
|
+
O.palette.error.main,
|
|
71
|
+
O.palette.info.main,
|
|
72
|
+
"#8e24aa",
|
|
73
|
+
"#00897b",
|
|
74
|
+
"#f06292",
|
|
75
|
+
"#a1887f"
|
|
76
|
+
], F = v && v.length > 0 ? v : ue, I = t((e, t) => {
|
|
77
|
+
let n = y?.[e];
|
|
78
|
+
return n?.fill ? n.fill : F[t % F.length];
|
|
79
|
+
}, [y, F]), L = r(() => w === "value" ? [...c].sort((e, t) => p.sum(j, (e) => t.values[e] ?? 0) - p.sum(j, (t) => e.values[t] ?? 0)) : w === "label" ? [...c].sort((e, t) => e.label.localeCompare(t.label, void 0, {
|
|
80
|
+
numeric: !0,
|
|
81
|
+
sensitivity: "base"
|
|
82
|
+
})) : c, [
|
|
83
|
+
c,
|
|
84
|
+
w,
|
|
85
|
+
j
|
|
86
|
+
]), R = r(() => p.max(L, (e) => p.sum(j, (t) => e.values[t] ?? 0)) ?? 0, [L, j]), z = r(() => p.scaleBand().domain(L.map((e) => e.id)).range([0, 2 * Math.PI]).padding(_), [L, _]), B = r(() => p.scaleLinear().domain([0, R]).range([P, N]), [
|
|
87
|
+
P,
|
|
88
|
+
N,
|
|
89
|
+
R
|
|
90
|
+
]), V = r(() => M ? [] : p.stack().keys(j).value((e, t) => e.values[t] ?? 0).order(p.stackOrderNone).offset(p.stackOffsetNone)(L), [
|
|
91
|
+
M,
|
|
92
|
+
L,
|
|
93
|
+
j
|
|
94
|
+
]), H = r(() => p.arc().startAngle((e) => e.x0).endAngle((e) => e.x1).innerRadius((e) => e.y0).outerRadius((e) => e.y1).padAngle(.008).padRadius(P + 10), [P]), U = r(() => R > 0 ? p.ticks(0, R, S).filter((e) => e > 0) : [], [R, S]), de = se ?? ee, [W, G] = a(1), K = i(null), [q, J] = a(`-${h / 2} -${h / 2} ${h} ${h}`), fe = r(() => {
|
|
95
|
+
if (W === 1) return q;
|
|
96
|
+
let [e, t, n, r] = q.split(" ").map(Number), i = n / W, a = r / W;
|
|
97
|
+
return `${e + (n - i) / 2} ${t + (r - a) / 2} ${i} ${a}`;
|
|
98
|
+
}, [q, W]);
|
|
99
|
+
n(() => {
|
|
100
|
+
let e = K.current;
|
|
101
|
+
if (!e) return;
|
|
102
|
+
let t = requestAnimationFrame(() => {
|
|
103
|
+
try {
|
|
104
|
+
let t = e.getBBox();
|
|
105
|
+
J(`${t.x - 4} ${t.y - 4} ${t.width + 8} ${t.height + 8}`);
|
|
106
|
+
} catch {
|
|
107
|
+
J(`-${h / 2} -${h / 2} ${h} ${h}`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return () => cancelAnimationFrame(t);
|
|
111
|
+
}, [
|
|
112
|
+
h,
|
|
113
|
+
c,
|
|
114
|
+
m,
|
|
115
|
+
P,
|
|
116
|
+
N,
|
|
117
|
+
b,
|
|
118
|
+
x
|
|
119
|
+
]), n(() => {
|
|
120
|
+
if (E) return;
|
|
121
|
+
let e = (e) => {
|
|
122
|
+
e.key === "Escape" && G(1);
|
|
123
|
+
};
|
|
124
|
+
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
125
|
+
}, [E]);
|
|
126
|
+
let pe = t((e) => {
|
|
127
|
+
if (!T || E || !e.ctrlKey) return;
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
let t = e.deltaY < 0 ? 1.15 : 1 / 1.15;
|
|
130
|
+
G((e) => Math.max(.25, Math.min(8, e * t)));
|
|
131
|
+
}, [T, E]), me = -(A.length * 16) / 2, Y = Math.max(0, P * 2 - 10 - 5 - 10), X = 9 * .57, he = A.reduce((e, t) => Math.max(e, (t.label ?? t.key).length), 0), ge = -((15 + Math.min(he * X, Y)) / 2), Z = Math.max(1, Math.floor(Y / X)), _e = (e) => e.length <= Z ? e : `${e.slice(0, Z - 1)}…`, ve = {
|
|
132
|
+
followCursor: !0,
|
|
133
|
+
enterDelay: 50,
|
|
134
|
+
enterNextDelay: 0,
|
|
135
|
+
disableHoverListener: E,
|
|
136
|
+
slotProps: { tooltip: { sx: { maxWidth: 240 } } }
|
|
137
|
+
}, Q = O.palette.text.primary, $ = O.palette.divider, ye = O.typography.fontFamily;
|
|
138
|
+
return /* @__PURE__ */ d(o, {
|
|
139
|
+
style: { opacity: E ? .5 : 1 },
|
|
140
|
+
sx: {
|
|
141
|
+
display: "inline-flex",
|
|
142
|
+
cursor: E ? "not-allowed" : "default",
|
|
143
|
+
userSelect: "none"
|
|
144
|
+
},
|
|
145
|
+
children: /* @__PURE__ */ d("svg", {
|
|
146
|
+
width: h,
|
|
147
|
+
height: h,
|
|
148
|
+
viewBox: fe,
|
|
149
|
+
onWheel: pe,
|
|
150
|
+
style: {
|
|
151
|
+
fontFamily: ye ?? "sans-serif",
|
|
152
|
+
overflow: T && W > 1 ? "hidden" : "visible"
|
|
153
|
+
},
|
|
154
|
+
role: "img",
|
|
155
|
+
"aria-label": "Radial stacked bar chart",
|
|
156
|
+
children: /* @__PURE__ */ f("g", {
|
|
157
|
+
ref: K,
|
|
158
|
+
children: [M && /* @__PURE__ */ d("text", {
|
|
159
|
+
textAnchor: "middle",
|
|
160
|
+
dy: "0.35em",
|
|
161
|
+
fontSize: 13,
|
|
162
|
+
fill: O.palette.text.secondary,
|
|
163
|
+
children: k.noData
|
|
164
|
+
}), !M && /* @__PURE__ */ f(u, { children: [
|
|
165
|
+
x && U.map((e) => /* @__PURE__ */ d("circle", {
|
|
166
|
+
r: B(e),
|
|
167
|
+
fill: "none",
|
|
168
|
+
stroke: $,
|
|
169
|
+
strokeWidth: .5,
|
|
170
|
+
strokeDasharray: "3 2"
|
|
171
|
+
}, `grid-${e}`)),
|
|
172
|
+
x && /* @__PURE__ */ d("circle", {
|
|
173
|
+
r: P,
|
|
174
|
+
fill: "none",
|
|
175
|
+
stroke: $,
|
|
176
|
+
strokeWidth: .75
|
|
177
|
+
}),
|
|
178
|
+
x && re && U.map((e) => /* @__PURE__ */ d("text", {
|
|
179
|
+
x: 0,
|
|
180
|
+
y: -B(e),
|
|
181
|
+
textAnchor: "middle",
|
|
182
|
+
dy: "-3",
|
|
183
|
+
fontSize: 9,
|
|
184
|
+
fill: O.palette.text.secondary,
|
|
185
|
+
pointerEvents: "none",
|
|
186
|
+
children: de(e)
|
|
187
|
+
}, `gv-${e}`)),
|
|
188
|
+
V.map((e, t) => {
|
|
189
|
+
let n = e.key, r = A[t]?.label ?? n, i = I(n, t);
|
|
190
|
+
return e.map((e, t) => {
|
|
191
|
+
let a = e.data, o = z(a.id) ?? 0, c = o + z.bandwidth(), l = B(e[0]), u = B(e[1]);
|
|
192
|
+
if (u - l < .5) return null;
|
|
193
|
+
let f = H({
|
|
194
|
+
x0: o,
|
|
195
|
+
x1: c,
|
|
196
|
+
y0: l,
|
|
197
|
+
y1: u
|
|
198
|
+
}) ?? "";
|
|
199
|
+
return /* @__PURE__ */ d(s, {
|
|
200
|
+
...ve,
|
|
201
|
+
placement: "top",
|
|
202
|
+
title: /* @__PURE__ */ d(ne, {
|
|
203
|
+
barData: a,
|
|
204
|
+
seriesKey: n,
|
|
205
|
+
seriesLabel: r,
|
|
206
|
+
decimals: ie,
|
|
207
|
+
decimalSep: ae,
|
|
208
|
+
thousandSep: oe
|
|
209
|
+
}),
|
|
210
|
+
children: /* @__PURE__ */ d("path", {
|
|
211
|
+
d: f,
|
|
212
|
+
fill: i,
|
|
213
|
+
style: {
|
|
214
|
+
cursor: E ? "not-allowed" : "pointer",
|
|
215
|
+
transition: "opacity 0.15s"
|
|
216
|
+
},
|
|
217
|
+
onMouseEnter: (e) => {
|
|
218
|
+
E || (e.currentTarget.style.opacity = "0.75");
|
|
219
|
+
},
|
|
220
|
+
onMouseLeave: (e) => {
|
|
221
|
+
e.currentTarget.style.opacity = "1";
|
|
222
|
+
},
|
|
223
|
+
onClick: (e) => {
|
|
224
|
+
if (E) return;
|
|
225
|
+
let t = p.sum(j, (e) => a.values[e] ?? 0), r = {
|
|
226
|
+
id: a.id,
|
|
227
|
+
label: a.label,
|
|
228
|
+
seriesKey: n,
|
|
229
|
+
value: a.values[n] ?? 0,
|
|
230
|
+
total: t,
|
|
231
|
+
values: a.values
|
|
232
|
+
};
|
|
233
|
+
ce?.(r, e);
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
}, `${n}-${t}`);
|
|
237
|
+
});
|
|
238
|
+
}),
|
|
239
|
+
b && L.map((e) => {
|
|
240
|
+
let t = (z(e.id) ?? 0) + z.bandwidth() / 2, n = N + 8, r = n * Math.sin(t), i = -n * Math.cos(t), a = t * 180 / Math.PI;
|
|
241
|
+
return /* @__PURE__ */ d("text", {
|
|
242
|
+
x: r,
|
|
243
|
+
y: i,
|
|
244
|
+
transform: `rotate(${a < 180 ? a - 90 : a + 90},${r},${i})`,
|
|
245
|
+
textAnchor: "middle",
|
|
246
|
+
dominantBaseline: "central",
|
|
247
|
+
fontSize: 11,
|
|
248
|
+
fill: Q,
|
|
249
|
+
pointerEvents: "none",
|
|
250
|
+
children: e.label
|
|
251
|
+
}, `lbl-${e.id}`);
|
|
252
|
+
}),
|
|
253
|
+
C && /* @__PURE__ */ d("g", {
|
|
254
|
+
pointerEvents: "none",
|
|
255
|
+
transform: `translate(${ge},0)`,
|
|
256
|
+
children: A.map((e, t) => {
|
|
257
|
+
let n = I(e.key, t), r = me + t * 16, i = _e(e.label ?? e.key);
|
|
258
|
+
return /* @__PURE__ */ f("g", {
|
|
259
|
+
transform: `translate(0,${r})`,
|
|
260
|
+
children: [/* @__PURE__ */ d("rect", {
|
|
261
|
+
x: 0,
|
|
262
|
+
y: 0,
|
|
263
|
+
width: 10,
|
|
264
|
+
height: 10,
|
|
265
|
+
fill: n,
|
|
266
|
+
rx: 2
|
|
267
|
+
}), /* @__PURE__ */ d("text", {
|
|
268
|
+
x: 15,
|
|
269
|
+
y: 10 / 2,
|
|
270
|
+
dominantBaseline: "central",
|
|
271
|
+
fontSize: 9,
|
|
272
|
+
fill: Q,
|
|
273
|
+
children: i
|
|
274
|
+
})]
|
|
275
|
+
}, `leg-${e.key}`);
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
] })]
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
h.displayName = "RadialStackedBarChart";
|
|
284
|
+
//#endregion
|
|
285
|
+
export { h as RadialStackedBarChart };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/** One series (stack layer) — key is the data field name, label overrides the legend text */
|
|
2
|
+
export type RadialStackedBarSeries = {
|
|
3
|
+
key: string;
|
|
4
|
+
label?: string;
|
|
5
|
+
};
|
|
6
|
+
/** One bar (e.g. one country, state, or category) */
|
|
7
|
+
export type RadialStackedBarData = {
|
|
8
|
+
/** Unique identifier — used as React key and in callback payloads */
|
|
9
|
+
id: string;
|
|
10
|
+
/** Outer-edge label shown next to the bar */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Value per series key — missing keys are treated as 0 */
|
|
13
|
+
values: Record<string, number>;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Per-series color overrides.
|
|
17
|
+
* Key = series key. Value = color config — null / omit = use palette.
|
|
18
|
+
* Example: `{ "65 Years and Over": { fill: "#1565C0" } }`
|
|
19
|
+
*/
|
|
20
|
+
export type RadialStackedBarColorConfigs = Record<string, {
|
|
21
|
+
fill?: string;
|
|
22
|
+
} | null>;
|
|
23
|
+
/** Payload delivered to `onBarClick` */
|
|
24
|
+
export type RadialStackedBarBarInfo = {
|
|
25
|
+
id: string;
|
|
26
|
+
label: string;
|
|
27
|
+
/** Series key that was clicked */
|
|
28
|
+
seriesKey: string;
|
|
29
|
+
/** Value of the clicked segment */
|
|
30
|
+
value: number;
|
|
31
|
+
/** Total bar value (sum of all series) */
|
|
32
|
+
total: number;
|
|
33
|
+
/** All values for this bar */
|
|
34
|
+
values: Record<string, number>;
|
|
35
|
+
};
|
|
36
|
+
export type RadialStackedBarChartTranslation = {
|
|
37
|
+
noData: string;
|
|
38
|
+
};
|
|
39
|
+
export declare const DEFAULT_RADIAL_STACKED_BAR_CHART_TRANSLATION: RadialStackedBarChartTranslation;
|
|
40
|
+
export type RadialStackedBarChartProps = {
|
|
41
|
+
/** Array of bars — each bar has a label and a value per series key */
|
|
42
|
+
data: RadialStackedBarData[];
|
|
43
|
+
/**
|
|
44
|
+
* Series definitions in stack order (innermost first).
|
|
45
|
+
* Pass `string[]` for quick setup — the string becomes both key and legend label.
|
|
46
|
+
* Pass `RadialStackedBarSeries[]` to customize the legend label independently from the key.
|
|
47
|
+
*/
|
|
48
|
+
keys: RadialStackedBarSeries[] | string[];
|
|
49
|
+
/** Width and height of the SVG in pixels (default: 500) */
|
|
50
|
+
size?: number;
|
|
51
|
+
/** Inner radius of the bar area in px — controls the center hole size (default: size * 0.18) */
|
|
52
|
+
innerRadius?: number;
|
|
53
|
+
/** Fractional gap between adjacent bars — 0 = no gap, 1 = all gap (default: 0.12) */
|
|
54
|
+
barPadding?: number;
|
|
55
|
+
/** Custom color palette for the series — falls back to MUI theme palette when omitted */
|
|
56
|
+
chartColors?: string[];
|
|
57
|
+
/** Per-series color overrides keyed by series key — takes precedence over `chartColors` */
|
|
58
|
+
colorConfig?: RadialStackedBarColorConfigs;
|
|
59
|
+
/** Show bar labels on the outer edge (default: true) */
|
|
60
|
+
showLabels?: boolean;
|
|
61
|
+
/** Show concentric grid rings (default: true) */
|
|
62
|
+
showGridLines?: boolean;
|
|
63
|
+
/** Number of concentric grid rings (default: 3) */
|
|
64
|
+
gridLineCount?: number;
|
|
65
|
+
/** Show value labels on the outermost grid ring (default: true) */
|
|
66
|
+
showGridValues?: boolean;
|
|
67
|
+
/** Show the series legend (default: true) */
|
|
68
|
+
showLegend?: boolean;
|
|
69
|
+
/** Sort bars by total descending ('value'), by label ascending ('label'), or keep original order ('none') — default: 'none' */
|
|
70
|
+
sortBy?: 'value' | 'label' | 'none';
|
|
71
|
+
/** Decimal places for values in tooltips (default: 0) */
|
|
72
|
+
valueDecimalCount?: number;
|
|
73
|
+
/** Decimal separator for values (default: '.') */
|
|
74
|
+
valueDecimalSeparator?: string;
|
|
75
|
+
/** Thousands separator for values (default: ',') */
|
|
76
|
+
valueThousandsSeparator?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Custom formatter for the grid ring value labels.
|
|
79
|
+
* Defaults to compact notation (e.g. 30000 → "30k", 3000000 → "3M").
|
|
80
|
+
* Example: `(v) => \`${(v / 1e6).toFixed(0)}M\``
|
|
81
|
+
*/
|
|
82
|
+
gridValueFormatter?: (value: number) => string;
|
|
83
|
+
/** Enable Ctrl/Cmd+Scroll visual zoom — content outside `size` is clipped (default: false) */
|
|
84
|
+
zoomable?: boolean;
|
|
85
|
+
/** Fired on click of any bar segment */
|
|
86
|
+
onBarClick?: (info: RadialStackedBarBarInfo, event: React.MouseEvent<SVGPathElement>) => void;
|
|
87
|
+
/** Disables all interactions (default: false) */
|
|
88
|
+
disabled?: boolean;
|
|
89
|
+
/** Override any translation string */
|
|
90
|
+
translation?: Partial<RadialStackedBarChartTranslation>;
|
|
91
|
+
};
|