@bluemarble/bi 0.6.3 → 0.6.5
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/{DashboardDevtools-Osu5V30U.js → DashboardDevtools-CMIuUYpw.js} +110 -8
- package/dist/DashboardDevtools-CMIuUYpw.js.map +1 -0
- package/dist/{ModelDictionary-Dc3_LKqr.js → ModelDictionary-CyeYU6ZJ.js} +123 -6
- package/dist/ModelDictionary-CyeYU6ZJ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/client/index.d.ts +37 -5
- package/dist/client/index.js +287 -174
- package/dist/client/index.js.map +1 -1
- package/dist/{codegen-DkfI6UJr.js → codegen-LoQzFAle.js} +3 -1
- package/dist/codegen-LoQzFAle.js.map +1 -0
- package/dist/{layout-DAFeHIuP.js → layout-CBzYDoqE.js} +93 -5
- package/dist/layout-CBzYDoqE.js.map +1 -0
- package/dist/{layout-Bn1OiRov.d.ts → layout-DsG-zRIO.d.ts} +48 -6
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/DashboardDevtools-Osu5V30U.js.map +0 -1
- package/dist/ModelDictionary-Dc3_LKqr.js.map +0 -1
- package/dist/codegen-DkfI6UJr.js.map +0 -1
- package/dist/layout-DAFeHIuP.js.map +0 -1
package/dist/client/index.js
CHANGED
|
@@ -1,143 +1,164 @@
|
|
|
1
|
-
import { T as rowRuleStyles, _ as tableColumns, f as isBuiltinChart, h as percentColumn, o as chartCardStyle, u as customChartProps } from "../layout-
|
|
2
|
-
import { a as formatCurrency, c as measureFormat, d as
|
|
1
|
+
import { T as rowRuleStyles, _ as tableColumns, f as isBuiltinChart, h as percentColumn, o as chartCardStyle, u as customChartProps } from "../layout-CBzYDoqE.js";
|
|
2
|
+
import { S as layoutItemKey, _ as useClickFilter, a as formatCurrency, c as measureFormat, d as OTHER_COLOR, f as SERIES_COLORS, g as seriesColor, h as numberOf, i as fieldLabel, l as measureFormatter, m as chartPlaceholder, n as INSPECT_ATTRIBUTE, o as formatMeasure, p as TooltipBox, r as useModelQuery, s as formatNumber, t as ModelDictionary, u as Legend, v as DashboardProvider, x as LAYOUT_ITEM_ATTRIBUTE, y as useDashboard } from "../ModelDictionary-CyeYU6ZJ.js";
|
|
3
3
|
import { Box, Checkbox, CircularProgress, FormControl, InputLabel, ListItemText, MenuItem, OutlinedInput, Paper, Select, Table, TableBody, TableCell, TableHead, TableRow, TableSortLabel, Typography } from "@mui/material";
|
|
4
4
|
import { Bar, BarChart as BarChart$1, CartesianGrid, Cell, ComposedChart, LabelList, Line, Pie, PieChart, ResponsiveContainer, Tooltip as Tooltip$1, XAxis, YAxis } from "recharts";
|
|
5
5
|
import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
//#region src/client/
|
|
7
|
+
//#region src/client/seriesPivot.ts
|
|
8
|
+
const EMPTY_SERIES_LABEL = "(em branco)";
|
|
9
|
+
const OTHERS$1 = "Outros";
|
|
10
|
+
const keyOf = (value) => value === null ? "\0" : String(value);
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
12
|
+
* Séries a partir do domínio (série × medida sem filtros): ordenadas pelo total, maior primeiro, e
|
|
13
|
+
* a cor segue essa ordem — como o domínio não depende dos filtros, cada valor tem sempre a mesma
|
|
14
|
+
* cor. Além de `max`, as menores viram "Outros" (a última vaga), em cinza.
|
|
11
15
|
*/
|
|
16
|
+
function seriesItems(domain, series, measure, max, format = String) {
|
|
17
|
+
const ranked = domain.map((row) => ({
|
|
18
|
+
value: row[series] ?? null,
|
|
19
|
+
amount: Math.abs(numberOf(row, measure) ?? 0)
|
|
20
|
+
})).sort((a, b) => b.amount - a.amount);
|
|
21
|
+
const limit = Math.min(Math.max(max, 2), 8);
|
|
22
|
+
const kept = ranked.length > limit ? ranked.slice(0, limit - 1) : ranked;
|
|
23
|
+
const items = kept.map((item, index) => ({
|
|
24
|
+
dataKey: `s${index}`,
|
|
25
|
+
value: item.value,
|
|
26
|
+
label: item.value === null ? EMPTY_SERIES_LABEL : format(item.value),
|
|
27
|
+
color: seriesColor(index)
|
|
28
|
+
}));
|
|
29
|
+
if (ranked.length > kept.length) items.push(othersItem(ranked.length - kept.length));
|
|
30
|
+
return items;
|
|
31
|
+
}
|
|
32
|
+
const othersItem = (count) => ({
|
|
33
|
+
dataKey: "outros",
|
|
34
|
+
value: null,
|
|
35
|
+
label: count ? `${OTHERS$1} (${count})` : OTHERS$1,
|
|
36
|
+
color: OTHER_COLOR,
|
|
37
|
+
others: true
|
|
38
|
+
});
|
|
12
39
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
16
|
-
const SERIES_COLORS = [
|
|
17
|
-
"#F58220",
|
|
18
|
-
"#2a78d6",
|
|
19
|
-
"#1baf7a",
|
|
20
|
-
"#eda100",
|
|
21
|
-
"#e87ba4",
|
|
22
|
-
"#008300",
|
|
23
|
-
"#6250d6",
|
|
24
|
-
"#e34948"
|
|
25
|
-
];
|
|
26
|
-
/** "Outros" (fatias agrupadas) fica neutro, fora da paleta. */
|
|
27
|
-
const OTHER_COLOR = "#9e9e9e";
|
|
28
|
-
const seriesColor = (index) => SERIES_COLORS[index % SERIES_COLORS.length];
|
|
29
|
-
/** Valor numérico de uma coluna da linha (`null` quando vazio). */
|
|
30
|
-
const numberOf = (row, key) => key && row[key] !== null && row[key] !== void 0 ? Number(row[key]) : null;
|
|
31
|
-
/**
|
|
32
|
-
* Filtro cruzado a partir do gráfico: clicar num valor filtra o dashboard por ele (Ctrl/Cmd+clique
|
|
33
|
-
* acumula; clicar de novo no único selecionado limpa). O gráfico deve ignorar o próprio filtro na
|
|
34
|
-
* consulta (`ignoreFilters`) e só destacar a seleção.
|
|
40
|
+
* Linhas (categoria, série, medida) → um ponto por categoria, na ordem da consulta, com o valor de
|
|
41
|
+
* cada série em `dataKey`. Valor fora das séries soma em "Outros" (criado se o domínio não tinha).
|
|
42
|
+
* Devolve só as séries com algum valor nos pontos (um filtro pode ter tirado uma delas).
|
|
35
43
|
*/
|
|
36
|
-
function
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
function pivotSeries(rows, dimension, series, measure, items, format = String) {
|
|
45
|
+
const all = [...items];
|
|
46
|
+
const byValue = new Map(all.filter((item) => !item.others).map((item) => [keyOf(item.value), item]));
|
|
47
|
+
const points = /* @__PURE__ */ new Map();
|
|
48
|
+
const used = /* @__PURE__ */ new Set();
|
|
49
|
+
for (const row of rows) {
|
|
50
|
+
const value = row[dimension] ?? "";
|
|
51
|
+
const key = String(value);
|
|
52
|
+
let point = points.get(key);
|
|
53
|
+
if (!point) {
|
|
54
|
+
point = {
|
|
55
|
+
value,
|
|
56
|
+
label: format(value)
|
|
57
|
+
};
|
|
58
|
+
for (const item of all) point[item.dataKey] = 0;
|
|
59
|
+
points.set(key, point);
|
|
51
60
|
}
|
|
61
|
+
let item = byValue.get(keyOf(row[series] ?? null));
|
|
62
|
+
if (!item) {
|
|
63
|
+
item = all.find((other) => other.others);
|
|
64
|
+
if (!item) {
|
|
65
|
+
item = othersItem();
|
|
66
|
+
all.push(item);
|
|
67
|
+
for (const other of points.values()) other[item.dataKey] = 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const amount = numberOf(row, measure);
|
|
71
|
+
if (amount) {
|
|
72
|
+
point[item.dataKey] = Number(point[item.dataKey] ?? 0) + amount;
|
|
73
|
+
used.add(item.dataKey);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
points: [...points.values()],
|
|
78
|
+
items: all.filter((item) => used.has(item.dataKey))
|
|
52
79
|
};
|
|
53
80
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
size: 14,
|
|
80
|
-
sx: { ml: "auto" }
|
|
81
|
-
})]
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
/** Erro / carregando / sem dados no lugar do gráfico; `null` quando há o que desenhar (função, não componente). */
|
|
85
|
-
function chartPlaceholder({ height, error, isLoading, empty }) {
|
|
86
|
-
if (!error && !empty) return null;
|
|
87
|
-
return /* @__PURE__ */ jsx(Box, {
|
|
88
|
-
sx: {
|
|
89
|
-
height,
|
|
90
|
-
display: "flex",
|
|
91
|
-
alignItems: "center",
|
|
92
|
-
justifyContent: "center",
|
|
93
|
-
color: error ? "error.main" : "#999"
|
|
94
|
-
},
|
|
95
|
-
children: error ? "Erro ao carregar o gráfico" : isLoading ? "Carregando…" : "Sem dados para os filtros selecionados"
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
/** `caption`: o que é o título (ex.: o rótulo da dimensão), em cinza acima dele. */
|
|
99
|
-
function TooltipBox({ title, caption, children }) {
|
|
100
|
-
return /* @__PURE__ */ jsxs(Box, {
|
|
101
|
-
sx: {
|
|
102
|
-
bgcolor: "#fff",
|
|
103
|
-
border: "1px solid #ccc",
|
|
104
|
-
p: 1,
|
|
105
|
-
fontSize: 13
|
|
106
|
-
},
|
|
107
|
-
children: [
|
|
108
|
-
caption && /* @__PURE__ */ jsx(Box, {
|
|
109
|
-
sx: {
|
|
110
|
-
fontSize: 11,
|
|
111
|
-
color: "#777"
|
|
112
|
-
},
|
|
113
|
-
children: caption
|
|
114
|
-
}),
|
|
115
|
-
/* @__PURE__ */ jsx(Box, {
|
|
116
|
-
sx: { fontWeight: 700 },
|
|
117
|
-
children: title
|
|
118
|
-
}),
|
|
119
|
-
children
|
|
120
|
-
]
|
|
121
|
-
});
|
|
81
|
+
/** Soma das séries de um ponto (a altura da barra empilhada). */
|
|
82
|
+
const pointTotal = (point, items) => items.reduce((total, item) => total + Number(point[item.dataKey] ?? 0), 0);
|
|
83
|
+
/**
|
|
84
|
+
* Todas as categorias da dimensão (consulta só da dimensão, sem medidas), na ordem dela, com os
|
|
85
|
+
* valores dos pontos que vieram da consulta e zero nas que não têm dado (o "Mostrar itens sem
|
|
86
|
+
* dados" do Power BI). Ponto fora da lista (não deveria haver) vai no fim.
|
|
87
|
+
*/
|
|
88
|
+
function fillCategories(points, categories, dimension, dataKeys, format = String) {
|
|
89
|
+
const byValue = new Map(points.map((point) => [String(point.value), point]));
|
|
90
|
+
const seen = /* @__PURE__ */ new Set();
|
|
91
|
+
const filled = [];
|
|
92
|
+
for (const row of categories) {
|
|
93
|
+
const value = row[dimension] ?? "";
|
|
94
|
+
const key = String(value);
|
|
95
|
+
if (seen.has(key)) continue;
|
|
96
|
+
seen.add(key);
|
|
97
|
+
const point = byValue.get(key);
|
|
98
|
+
if (point) filled.push(point);
|
|
99
|
+
else filled.push({
|
|
100
|
+
value,
|
|
101
|
+
label: format(value),
|
|
102
|
+
...Object.fromEntries(dataKeys.map((dataKey) => [dataKey, 0]))
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return [...filled, ...points.filter((point) => !seen.has(String(point.value)))];
|
|
122
106
|
}
|
|
123
107
|
//#endregion
|
|
124
108
|
//#region src/client/BarChart.tsx
|
|
125
109
|
const MAX_VALUE_LABELS = 30;
|
|
110
|
+
/** Coluna do total da pilha no ponto (para o rótulo do `showTotal`). */
|
|
111
|
+
const TOTAL_KEY = "\0total";
|
|
112
|
+
/** Posição do valor no Recharts, conforme a orientação. */
|
|
113
|
+
const LABEL_POSITION = {
|
|
114
|
+
vertical: {
|
|
115
|
+
outside: "top",
|
|
116
|
+
insideEnd: "insideTop",
|
|
117
|
+
center: "center",
|
|
118
|
+
insideBase: "insideBottom"
|
|
119
|
+
},
|
|
120
|
+
horizontal: {
|
|
121
|
+
outside: "right",
|
|
122
|
+
insideEnd: "insideRight",
|
|
123
|
+
center: "center",
|
|
124
|
+
insideBase: "insideLeft"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/** Chave de uma série em `colors`: o valor, "(em branco)" ou "Outros". */
|
|
128
|
+
const seriesColorKey = (item) => item.others ? "Outros" : item.value === null ? EMPTY_SERIES_LABEL : String(item.value);
|
|
126
129
|
/** Altura de cada categoria no modo horizontal (barras finas, com respiro). */
|
|
127
130
|
const ROW_HEIGHT = 28;
|
|
128
131
|
/**
|
|
129
132
|
* Barras de uma ou mais medidas por uma dimensão. Ex.:
|
|
130
133
|
* `<BarChart dimension="navio" measures={['custoTotal']} orientation="horizontal" sort="desc" limit={10} />`.
|
|
134
|
+
* Com `series`, uma medida quebrada pelos valores de outra dimensão:
|
|
135
|
+
* `<BarChart dimension="mes" series="tipo_descarga" measures={['volume']} stacked />`.
|
|
131
136
|
*/
|
|
132
|
-
function BarChart({ dimension, measures, orientation = "vertical", stacked = false, sort = "dimension", limit, formatDimension = String, hideEmpty = true, showValues = true, clickToFilter = false, height = 360, labels, formats: formatsByMeasure, queryLabel }) {
|
|
133
|
-
const { meta } = useDashboard();
|
|
137
|
+
function BarChart({ dimension, measures: measureList, series, formatSeries = String, maxSeries = 6, orientation = "vertical", stacked = false, sort = "dimension", limit, formatDimension = String, hideEmpty = true, showEmpty = false, showValues = true, valuePosition = "outside", valueColor, valueSize = 12, showTotal = false, colors, clickToFilter = false, height = 360, labels, formats: formatsByMeasure, queryLabel }) {
|
|
138
|
+
const { meta, filters, setFilter } = useDashboard();
|
|
139
|
+
const measures = series ? measureList.slice(0, 1) : measureList;
|
|
134
140
|
const { rows, isLoading, error, inspectProps } = useModelQuery({
|
|
135
|
-
dimensions: [dimension],
|
|
141
|
+
dimensions: series ? [dimension, series] : [dimension],
|
|
136
142
|
measures,
|
|
137
|
-
ignoreFilters: clickToFilter ? [dimension] : void 0,
|
|
143
|
+
ignoreFilters: clickToFilter ? series ? [dimension, series] : [dimension] : void 0,
|
|
138
144
|
label: queryLabel ?? `BarChart · ${dimension}`
|
|
139
145
|
});
|
|
146
|
+
const domain = useModelQuery({
|
|
147
|
+
dimensions: series ? [series] : [],
|
|
148
|
+
measures,
|
|
149
|
+
ignoreFilters: Object.keys(filters),
|
|
150
|
+
label: `${queryLabel ?? `BarChart · ${dimension}`} · séries`,
|
|
151
|
+
skip: !series
|
|
152
|
+
});
|
|
153
|
+
const table = meta?.dimensions[dimension]?.table;
|
|
154
|
+
const categories = useModelQuery({
|
|
155
|
+
dimensions: [dimension],
|
|
156
|
+
ignoreFilters: Object.keys(filters).filter((name) => clickToFilter && name === dimension || !table || meta?.dimensions[name]?.table !== table),
|
|
157
|
+
label: `${queryLabel ?? `BarChart · ${dimension}`} · categorias`,
|
|
158
|
+
skip: !showEmpty || !meta
|
|
159
|
+
});
|
|
140
160
|
const filter = useClickFilter(dimension, clickToFilter);
|
|
161
|
+
const seriesFilter = useClickFilter(series, clickToFilter);
|
|
141
162
|
const formats = measures.map((measure) => measureFormatter(meta, measure, 0, formatsByMeasure));
|
|
142
163
|
const labelOf = (name) => fieldLabel(meta, name, labels);
|
|
143
164
|
const axisFormat = measures[0] ? measureFormat(meta, measures[0], formatsByMeasure) : void 0;
|
|
@@ -145,61 +166,143 @@ function BarChart({ dimension, measures, orientation = "vertical", stacked = fal
|
|
|
145
166
|
...axisFormat,
|
|
146
167
|
decimals: axisFormat?.compact ? 1 : 0
|
|
147
168
|
}, 0);
|
|
169
|
+
let data;
|
|
170
|
+
let bars;
|
|
148
171
|
const first = measures[0];
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
172
|
+
if (series && first) {
|
|
173
|
+
const format = formats[0] ?? String;
|
|
174
|
+
const pivot = pivotSeries(rows, dimension, series, first, seriesItems(domain.rows, series, first, maxSeries, formatSeries), formatDimension);
|
|
175
|
+
bars = pivot.items.map((item) => ({
|
|
176
|
+
...item,
|
|
177
|
+
color: colors?.[seriesColorKey(item)] ?? item.color,
|
|
178
|
+
item,
|
|
179
|
+
format
|
|
180
|
+
}));
|
|
181
|
+
data = pivot.points;
|
|
182
|
+
} else {
|
|
183
|
+
bars = measures.map((measure, index) => ({
|
|
184
|
+
dataKey: measure,
|
|
185
|
+
label: labelOf(measure),
|
|
186
|
+
color: colors?.[measure] ?? seriesColor(index),
|
|
187
|
+
format: formats[index] ?? String
|
|
188
|
+
}));
|
|
189
|
+
data = rows.map((row) => {
|
|
190
|
+
const value = row[dimension] ?? "";
|
|
191
|
+
const point = {
|
|
192
|
+
value,
|
|
193
|
+
label: formatDimension(value)
|
|
194
|
+
};
|
|
195
|
+
for (const measure of measures) point[measure] = numberOf(row, measure) ?? 0;
|
|
196
|
+
return point;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (showEmpty) data = fillCategories(data, categories.rows, dimension, bars.map((bar) => bar.dataKey), formatDimension);
|
|
200
|
+
else if (hideEmpty) data = data.filter((point) => bars.some((bar) => Number(point[bar.dataKey])));
|
|
201
|
+
const sortValue = (point) => series ? pointTotal(point, bars.map((bar) => bar.item).filter((item) => !!item)) : Number(point[first ?? ""]);
|
|
202
|
+
if (sort !== "dimension" && first) data = [...data].sort((a, b) => sort === "desc" ? sortValue(b) - sortValue(a) : sortValue(a) - sortValue(b));
|
|
159
203
|
if (limit) data = data.slice(0, limit);
|
|
160
204
|
const horizontal = orientation === "horizontal";
|
|
161
205
|
const showLabels = showValues && data.length <= MAX_VALUE_LABELS;
|
|
206
|
+
const segmentLabels = showLabels && (!stacked || valuePosition !== "outside");
|
|
207
|
+
const totalLabel = showLabels && stacked && showTotal && bars.length > 0;
|
|
208
|
+
const labelsOutside = segmentLabels && valuePosition === "outside" || totalLabel;
|
|
209
|
+
if (totalLabel) data = data.map((point) => ({
|
|
210
|
+
...point,
|
|
211
|
+
[TOTAL_KEY]: bars.reduce((total, bar) => total + Number(point[bar.dataKey] ?? 0), 0)
|
|
212
|
+
}));
|
|
213
|
+
const labelStyle = (outside) => ({
|
|
214
|
+
fontSize: valueSize,
|
|
215
|
+
fill: valueColor ?? (outside ? "#333" : "#fff"),
|
|
216
|
+
fontWeight: 600
|
|
217
|
+
});
|
|
218
|
+
const position = LABEL_POSITION[orientation === "horizontal" ? "horizontal" : "vertical"];
|
|
162
219
|
const chartHeight = horizontal ? Math.max(height, data.length * ROW_HEIGHT + 24) : height;
|
|
163
220
|
const longest = Math.max(0, ...data.map((point) => point.label.length));
|
|
221
|
+
const loading = isLoading || domain.isLoading || categories.isLoading || showEmpty && !meta;
|
|
164
222
|
const state = chartPlaceholder({
|
|
165
223
|
height,
|
|
166
|
-
error,
|
|
167
|
-
isLoading,
|
|
224
|
+
error: error ?? domain.error ?? categories.error,
|
|
225
|
+
isLoading: loading,
|
|
168
226
|
empty: !data.length
|
|
169
227
|
});
|
|
170
228
|
const ChartTooltip = ({ active, payload }) => {
|
|
171
229
|
if (!active || !payload?.length) return null;
|
|
172
230
|
const point = payload[0].payload;
|
|
173
|
-
|
|
231
|
+
const shown = series ? bars.filter((bar) => Number(point[bar.dataKey])) : bars;
|
|
232
|
+
const format = bars[0]?.format ?? String;
|
|
233
|
+
return /* @__PURE__ */ jsxs(TooltipBox, {
|
|
174
234
|
title: point.label,
|
|
175
235
|
caption: labelOf(dimension),
|
|
176
|
-
children:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
236
|
+
children: [
|
|
237
|
+
series && first && /* @__PURE__ */ jsxs(Box, {
|
|
238
|
+
sx: {
|
|
239
|
+
fontSize: 11,
|
|
240
|
+
color: "#777",
|
|
241
|
+
mt: .5
|
|
242
|
+
},
|
|
243
|
+
children: [
|
|
244
|
+
labelOf(first),
|
|
245
|
+
" por ",
|
|
246
|
+
labelOf(series).toLocaleLowerCase("pt-BR")
|
|
247
|
+
]
|
|
248
|
+
}),
|
|
249
|
+
shown.map((bar) => /* @__PURE__ */ jsxs(Box, {
|
|
250
|
+
sx: {
|
|
251
|
+
display: "flex",
|
|
252
|
+
alignItems: "center",
|
|
253
|
+
gap: .75
|
|
254
|
+
},
|
|
255
|
+
children: [
|
|
256
|
+
/* @__PURE__ */ jsx(Box, { sx: {
|
|
257
|
+
width: 8,
|
|
258
|
+
height: 8,
|
|
259
|
+
borderRadius: "50%",
|
|
260
|
+
bgcolor: bar.color
|
|
261
|
+
} }),
|
|
262
|
+
bar.label,
|
|
263
|
+
": ",
|
|
264
|
+
bar.format(Number(point[bar.dataKey]))
|
|
265
|
+
]
|
|
266
|
+
}, bar.dataKey)),
|
|
267
|
+
series && stacked && shown.length > 1 && /* @__PURE__ */ jsxs(Box, {
|
|
268
|
+
sx: {
|
|
269
|
+
fontWeight: 700,
|
|
270
|
+
mt: .5
|
|
271
|
+
},
|
|
272
|
+
children: ["Total: ", format(shown.reduce((total, bar) => total + Number(point[bar.dataKey]), 0))]
|
|
273
|
+
})
|
|
274
|
+
]
|
|
194
275
|
});
|
|
195
276
|
};
|
|
196
|
-
const handleClick = (index, event) => {
|
|
277
|
+
const handleClick = (index, bar, event) => {
|
|
197
278
|
event.stopPropagation();
|
|
198
279
|
const point = data[index];
|
|
199
|
-
if (point)
|
|
280
|
+
if (!point) return;
|
|
281
|
+
const value = bar.item?.value;
|
|
282
|
+
if (!series || value === null || value === void 0) {
|
|
283
|
+
filter.toggle(point.value, event);
|
|
284
|
+
seriesFilter.clear();
|
|
285
|
+
} else if (event.ctrlKey || event.metaKey) {
|
|
286
|
+
filter.toggle(point.value, event);
|
|
287
|
+
seriesFilter.toggle(value, event);
|
|
288
|
+
} else {
|
|
289
|
+
const sole = (selected, item) => selected.length === 1 && selected[0] === item;
|
|
290
|
+
const again = sole(filter.selected, point.value) && sole(seriesFilter.selected, value);
|
|
291
|
+
setFilter(dimension, again ? [] : [point.value]);
|
|
292
|
+
setFilter(series, again ? [] : [value]);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const isDimmed = (point, bar) => {
|
|
296
|
+
if (filter.isDimmed(point.value)) return true;
|
|
297
|
+
if (!bar.item || !seriesFilter.selected.length) return false;
|
|
298
|
+
return bar.item.value === null || seriesFilter.isDimmed(bar.item.value);
|
|
299
|
+
};
|
|
300
|
+
const clearFilters = () => {
|
|
301
|
+
filter.clear();
|
|
302
|
+
seriesFilter.clear();
|
|
200
303
|
};
|
|
201
304
|
const radius = (index) => {
|
|
202
|
-
if (stacked && index <
|
|
305
|
+
if (stacked && index < bars.length - 1) return [
|
|
203
306
|
0,
|
|
204
307
|
0,
|
|
205
308
|
0,
|
|
@@ -221,17 +324,17 @@ function BarChart({ dimension, measures, orientation = "vertical", stacked = fal
|
|
|
221
324
|
...inspectProps,
|
|
222
325
|
sx: { minWidth: 0 },
|
|
223
326
|
children: [/* @__PURE__ */ jsx(Legend, {
|
|
224
|
-
items:
|
|
225
|
-
label
|
|
226
|
-
color
|
|
327
|
+
items: series || bars.length > 1 ? bars.map(({ label, color }) => ({
|
|
328
|
+
label,
|
|
329
|
+
color
|
|
227
330
|
})) : [],
|
|
228
|
-
loading
|
|
331
|
+
loading
|
|
229
332
|
}), state ?? /* @__PURE__ */ jsx(Box, {
|
|
230
|
-
onClick:
|
|
333
|
+
onClick: clearFilters,
|
|
231
334
|
sx: {
|
|
232
335
|
height,
|
|
233
336
|
overflowY: horizontal ? "auto" : "hidden",
|
|
234
|
-
opacity:
|
|
337
|
+
opacity: loading ? .5 : 1,
|
|
235
338
|
transition: "opacity .2s"
|
|
236
339
|
},
|
|
237
340
|
children: /* @__PURE__ */ jsx(Box, {
|
|
@@ -246,11 +349,11 @@ function BarChart({ dimension, measures, orientation = "vertical", stacked = fal
|
|
|
246
349
|
barCategoryGap: "20%",
|
|
247
350
|
margin: horizontal ? {
|
|
248
351
|
top: 4,
|
|
249
|
-
right:
|
|
352
|
+
right: labelsOutside ? 96 : 16,
|
|
250
353
|
left: 8,
|
|
251
354
|
bottom: 4
|
|
252
355
|
} : {
|
|
253
|
-
top:
|
|
356
|
+
top: labelsOutside ? valueSize + 12 : 8,
|
|
254
357
|
right: 8,
|
|
255
358
|
left: 8,
|
|
256
359
|
bottom: 0
|
|
@@ -302,27 +405,36 @@ function BarChart({ dimension, measures, orientation = "vertical", stacked = fal
|
|
|
302
405
|
content: /* @__PURE__ */ jsx(ChartTooltip, {}),
|
|
303
406
|
cursor: { fill: "rgba(0,0,0,.04)" }
|
|
304
407
|
}),
|
|
305
|
-
|
|
306
|
-
dataKey:
|
|
307
|
-
name:
|
|
308
|
-
fill:
|
|
408
|
+
bars.map((bar, index) => /* @__PURE__ */ jsxs(Bar, {
|
|
409
|
+
dataKey: bar.dataKey,
|
|
410
|
+
name: bar.label,
|
|
411
|
+
fill: bar.color,
|
|
309
412
|
stackId: stacked ? "stack" : void 0,
|
|
310
413
|
radius: radius(index),
|
|
311
414
|
maxBarSize: horizontal ? 18 : 56,
|
|
312
415
|
isAnimationActive: false,
|
|
313
|
-
onClick: clickToFilter ? (_bar, barIndex, event) => handleClick(barIndex, event) : void 0,
|
|
416
|
+
onClick: clickToFilter ? (_bar, barIndex, event) => handleClick(barIndex, bar, event) : void 0,
|
|
314
417
|
cursor: clickToFilter ? "pointer" : void 0,
|
|
315
|
-
children: [
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
418
|
+
children: [
|
|
419
|
+
clickToFilter && data.map((point) => /* @__PURE__ */ jsx(Cell, { fillOpacity: isDimmed(point, bar) ? .35 : 1 }, String(point.value))),
|
|
420
|
+
segmentLabels && /* @__PURE__ */ jsx(LabelList, {
|
|
421
|
+
dataKey: bar.dataKey,
|
|
422
|
+
position: position[valuePosition],
|
|
423
|
+
formatter: (value) => stacked && !Number(value) ? "" : bar.format(value),
|
|
424
|
+
style: labelStyle(valuePosition === "outside")
|
|
425
|
+
}),
|
|
426
|
+
totalLabel && index === bars.length - 1 && /* @__PURE__ */ jsx(LabelList, {
|
|
427
|
+
dataKey: TOTAL_KEY,
|
|
428
|
+
position: position.outside,
|
|
429
|
+
formatter: (value) => (bars[0]?.format ?? String)(value),
|
|
430
|
+
style: {
|
|
431
|
+
...labelStyle(true),
|
|
432
|
+
...valuePosition !== "outside" && { fill: "#333" },
|
|
433
|
+
fontWeight: 700
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
]
|
|
437
|
+
}, bar.dataKey))
|
|
326
438
|
]
|
|
327
439
|
})
|
|
328
440
|
})
|
|
@@ -1279,11 +1391,12 @@ function Chart({ chart, formatters }) {
|
|
|
1279
1391
|
});
|
|
1280
1392
|
}
|
|
1281
1393
|
case "bar": {
|
|
1282
|
-
const { id, type, title, span, card, formatDimension, ...props } = chart;
|
|
1394
|
+
const { id, type, title, span, card, formatDimension, formatSeries, ...props } = chart;
|
|
1283
1395
|
return /* @__PURE__ */ jsx(BarChart, {
|
|
1284
1396
|
...props,
|
|
1285
1397
|
queryLabel,
|
|
1286
|
-
formatDimension: formatter(formatDimension)
|
|
1398
|
+
formatDimension: formatter(formatDimension),
|
|
1399
|
+
formatSeries: formatter(formatSeries)
|
|
1287
1400
|
});
|
|
1288
1401
|
}
|
|
1289
1402
|
case "donut": {
|