@bluemarble/bi 0.4.0 → 0.5.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/{DashboardDevtools-7L93aV1-.js → DashboardDevtools-6v5HDe-A.js} +597 -236
- package/dist/DashboardDevtools-6v5HDe-A.js.map +1 -0
- package/dist/{ModelDictionary-Y7W9oK1J.js → ModelDictionary-D3JzCd6x.js} +81 -5
- package/dist/ModelDictionary-D3JzCd6x.js.map +1 -0
- package/dist/client/index.d.ts +10 -2
- package/dist/client/index.js +13 -80
- package/dist/client/index.js.map +1 -1
- package/dist/{layout-676sEvvb.js → layout-QuOYFmp1.js} +29 -5
- package/dist/layout-QuOYFmp1.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +1 -1
- package/dist/{types-HXAn-3I1.d.ts → types-FdrY0FOZ.d.ts} +13 -1
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
- package/dist/DashboardDevtools-7L93aV1-.js.map +0 -1
- package/dist/ModelDictionary-Y7W9oK1J.js.map +0 -1
- package/dist/layout-676sEvvb.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as measureHome, C as AGGREGATION_LABEL, E as isAggregate, M as parseColumnRef, O as validateModel, P as referencedTables, S as AGGREGATIONS, _ as validateLayout, a as FILTER_FIELDS, c as chartOfType, d as filterReferences, h as orderedColumns, i as CARD_FIELDS, j as modelEdges, l as chartReferences, m as layoutModelWarnings, n as CANVAS_PRESETS, p as layoutFileContent, r as CARDS_FIELDS, s as chartColumns, t as BUILTIN_CHART_TYPES, v as canonicalJson, w as VALUE_FORMATS, x as toModelCode } from "./layout-QuOYFmp1.js";
|
|
2
2
|
import { n as diffRows } from "./compare-DloZhg1T.js";
|
|
3
|
-
import {
|
|
4
|
-
import { Alert, Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItemButton, Menu, MenuItem, MenuList, Paper, Popper, Portal, Tab, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel, Tabs, TextField, ThemeProvider, ToggleButton, ToggleButtonGroup, Tooltip, Typography, createTheme } from "@mui/material";
|
|
3
|
+
import { d as useDashboard, f as errorMessage, h as parseLayoutItemKey, m as layoutItemKey, n as INSPECT_ATTRIBUTE, o as formatMeasure, p as LAYOUT_ITEM_ATTRIBUTE, t as ModelDictionary } from "./ModelDictionary-D3JzCd6x.js";
|
|
4
|
+
import { Alert, Autocomplete, Box, Button, Checkbox, Chip, CircularProgress, Collapse, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControlLabel, IconButton, List, ListItemButton, ListSubheader, Menu, MenuItem, MenuList, Paper, Popover, Popper, Portal, Tab, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TableSortLabel, Tabs, TextField, ThemeProvider, ToggleButton, ToggleButtonGroup, Tooltip, Typography, createTheme } from "@mui/material";
|
|
5
5
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { createPortal } from "react-dom";
|
|
@@ -960,8 +960,8 @@ const chartSummary = (chart, types) => {
|
|
|
960
960
|
};
|
|
961
961
|
/**
|
|
962
962
|
* Editor dos layouts da página (filtros do `<DashboardFilters>` e gráficos do `<DashboardCharts>`).
|
|
963
|
-
*
|
|
964
|
-
* grava o JSON do layout.
|
|
963
|
+
* Como no modelo: as edições ficam no editor até "Aplicar na página" (vira rascunho e a página
|
|
964
|
+
* re-renderiza com ele); "Salvar no arquivo" grava o JSON do layout com o que está no editor.
|
|
965
965
|
*/
|
|
966
966
|
function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
967
967
|
const { layouts } = devtools;
|
|
@@ -970,7 +970,8 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
970
970
|
const [layoutId, setLayoutId] = useState(null);
|
|
971
971
|
const entry = entries.find((item) => item.registered.id === layoutId) ?? entries[0] ?? null;
|
|
972
972
|
const reference = entry ? entry.draft ?? entry.registered : null;
|
|
973
|
-
const
|
|
973
|
+
const pendingOrReference = () => (entry && layouts.pendingOf(entry.registered.id)) ?? reference;
|
|
974
|
+
const [editing, setEditingState] = useState(pendingOrReference);
|
|
974
975
|
const [selection, setSelection] = useState(null);
|
|
975
976
|
const [sidebar, setSidebar] = useState(loadSidebar);
|
|
976
977
|
const [drag, setDrag] = useState(null);
|
|
@@ -981,7 +982,19 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
981
982
|
const [save, setSave] = useState(null);
|
|
982
983
|
const [addMenu, setAddMenu] = useState(null);
|
|
983
984
|
const referenceKey = canonicalJson(reference);
|
|
984
|
-
|
|
985
|
+
/** Muda o editor e guarda como pendente o que difere do que a página mostra. */
|
|
986
|
+
const setEditing = (next) => {
|
|
987
|
+
setEditingState(next);
|
|
988
|
+
if (entry) layouts.setPending(entry.registered.id, next && canonicalJson(next) !== referenceKey ? next : null);
|
|
989
|
+
};
|
|
990
|
+
const firstRun = useRef(true);
|
|
991
|
+
useEffect(() => {
|
|
992
|
+
if (firstRun.current) {
|
|
993
|
+
firstRun.current = false;
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
setEditingState(pendingOrReference());
|
|
997
|
+
}, [referenceKey]);
|
|
985
998
|
useEffect(() => {
|
|
986
999
|
const parsed = picked && parseLayoutItemKey(picked.item);
|
|
987
1000
|
if (!parsed) return;
|
|
@@ -1025,13 +1038,16 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1025
1038
|
const selected = selection?.kind === "filter" || selection?.kind === "canvas" || selection?.kind === "cards" ? null : charts.find((chart) => chart.id === selection?.key) ?? charts[0] ?? null;
|
|
1026
1039
|
const layoutKey = entry.registered.id;
|
|
1027
1040
|
const differsFromFile = canonicalJson(editing) !== canonicalJson(entry.registered);
|
|
1041
|
+
const dirty = canonicalJson(editing) !== referenceKey;
|
|
1028
1042
|
const update = (next) => {
|
|
1029
1043
|
setEditing(next);
|
|
1030
1044
|
setSave(null);
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1045
|
+
};
|
|
1046
|
+
const apply = () => {
|
|
1047
|
+
if (validateLayout(editing, chartTypes).length) return;
|
|
1048
|
+
devtools.store.expectRefetch();
|
|
1049
|
+
layouts.setPending(entry.registered.id, null);
|
|
1050
|
+
layouts.setDraft(entry.registered.id, editing);
|
|
1035
1051
|
};
|
|
1036
1052
|
const setCharts = (next) => update({
|
|
1037
1053
|
...editing,
|
|
@@ -1134,6 +1150,11 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1134
1150
|
} }
|
|
1135
1151
|
});
|
|
1136
1152
|
setSave(mode === "write" ? null : result.devtools?.save ?? null);
|
|
1153
|
+
if (mode === "write" && editing && entry) {
|
|
1154
|
+
devtools.store.expectRefetch();
|
|
1155
|
+
layouts.setPending(entry.registered.id, null);
|
|
1156
|
+
layouts.setDraft(entry.registered.id, editing);
|
|
1157
|
+
}
|
|
1137
1158
|
} catch (error) {
|
|
1138
1159
|
setActionError(errorMessage(error));
|
|
1139
1160
|
} finally {
|
|
@@ -1398,29 +1419,53 @@ function LayoutPanel({ devtools, model, capabilities, picked, onHover }) {
|
|
|
1398
1419
|
borderColor: "divider"
|
|
1399
1420
|
},
|
|
1400
1421
|
children: [
|
|
1422
|
+
dirty && /* @__PURE__ */ jsx(Typography, {
|
|
1423
|
+
sx: {
|
|
1424
|
+
fontSize: 12,
|
|
1425
|
+
color: "warning.main",
|
|
1426
|
+
mr: "auto"
|
|
1427
|
+
},
|
|
1428
|
+
children: "Edições não aplicadas"
|
|
1429
|
+
}),
|
|
1401
1430
|
/* @__PURE__ */ jsx(CopyButton, {
|
|
1402
1431
|
text: layoutFileContent(editing),
|
|
1403
1432
|
label: "Copiar JSON"
|
|
1404
1433
|
}),
|
|
1434
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1435
|
+
size: "small",
|
|
1436
|
+
disabled: !dirty,
|
|
1437
|
+
onClick: () => setEditing(reference),
|
|
1438
|
+
title: "Desfaz as edições ainda não aplicadas",
|
|
1439
|
+
children: "Desfazer"
|
|
1440
|
+
}),
|
|
1405
1441
|
/* @__PURE__ */ jsx(Button, {
|
|
1406
1442
|
size: "small",
|
|
1407
1443
|
color: "warning",
|
|
1408
|
-
disabled: !entry.draft
|
|
1444
|
+
disabled: !entry.draft,
|
|
1409
1445
|
onClick: () => {
|
|
1410
1446
|
devtools.store.expectRefetch();
|
|
1447
|
+
layouts.setPending(entry.registered.id, null);
|
|
1411
1448
|
layouts.setDraft(entry.registered.id, null);
|
|
1412
|
-
|
|
1449
|
+
setEditingState(entry.registered);
|
|
1413
1450
|
},
|
|
1414
|
-
title: "Volta ao layout do arquivo",
|
|
1451
|
+
title: "Volta a página e o editor ao layout do arquivo",
|
|
1415
1452
|
children: "Descartar rascunho"
|
|
1416
1453
|
}),
|
|
1417
1454
|
capabilities?.saveLayout && /* @__PURE__ */ jsx(Button, {
|
|
1418
1455
|
size: "small",
|
|
1419
|
-
variant: "
|
|
1456
|
+
variant: "outlined",
|
|
1420
1457
|
disabled: !differsFromFile || !!errors.length || busy,
|
|
1421
1458
|
onClick: () => saveFile("preview"),
|
|
1422
|
-
title: `Reescreve ${entry.registered.id}.json`,
|
|
1459
|
+
title: `Reescreve ${entry.registered.id}.json com o que está no editor`,
|
|
1423
1460
|
children: busy ? "Gerando…" : "Salvar no arquivo"
|
|
1461
|
+
}),
|
|
1462
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1463
|
+
size: "small",
|
|
1464
|
+
variant: "contained",
|
|
1465
|
+
disabled: !dirty || !!errors.length,
|
|
1466
|
+
onClick: apply,
|
|
1467
|
+
title: "Mostra as edições na página (sem gravar o arquivo)",
|
|
1468
|
+
children: "Aplicar na página"
|
|
1424
1469
|
})
|
|
1425
1470
|
]
|
|
1426
1471
|
}),
|
|
@@ -1646,11 +1691,13 @@ function SmallButton({ title, disabled, onClick, children }) {
|
|
|
1646
1691
|
}
|
|
1647
1692
|
function ChartForm({ chart, cards, types, model, formatters, columns, onChange }) {
|
|
1648
1693
|
const set = (key, value) => onChange((current) => withField(current, key, value));
|
|
1694
|
+
const setMany = (changes) => onChange((current) => Object.entries(changes).reduce((chart, [key, value]) => withField(chart, key, value), current));
|
|
1649
1695
|
const setCard = (key, value) => onChange((current) => withField(current, "card", nonEmpty(withField(current.card ?? {}, key, value))));
|
|
1650
1696
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FieldsForm, {
|
|
1651
1697
|
fields: types[chart.type]?.fields ?? {},
|
|
1652
1698
|
values: chart,
|
|
1653
1699
|
onSet: set,
|
|
1700
|
+
onSetMany: setMany,
|
|
1654
1701
|
model,
|
|
1655
1702
|
formatters,
|
|
1656
1703
|
columns: chartColumns(chart, types),
|
|
@@ -1704,7 +1751,7 @@ function FilterForm({ filter, model, formatters, columns, onChange }) {
|
|
|
1704
1751
|
});
|
|
1705
1752
|
}
|
|
1706
1753
|
/** Formulário gerado dos `FieldSpec` (campos do tipo de gráfico ou do filtro). */
|
|
1707
|
-
function FieldsForm({ fields, values, onSet: set, model, formatters, columns, formColumns, chosenDimensions, chosenMeasures = [], header }) {
|
|
1754
|
+
function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters, columns, formColumns, chosenDimensions, chosenMeasures = [], header }) {
|
|
1708
1755
|
const dimensions = Object.entries(model.dimensions).map(([name, dim]) => ({
|
|
1709
1756
|
name,
|
|
1710
1757
|
label: dim.label
|
|
@@ -1714,6 +1761,106 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1714
1761
|
label: measure.label
|
|
1715
1762
|
}));
|
|
1716
1763
|
const entries = Object.entries(fields);
|
|
1764
|
+
const keyOf = (kind) => entries.find(([, spec]) => spec.kind === kind)?.[0];
|
|
1765
|
+
const labelsKey = keyOf("labels");
|
|
1766
|
+
const formatsKey = keyOf("formats");
|
|
1767
|
+
const columnOptions = !!(labelsKey || formatsKey);
|
|
1768
|
+
const formattersKey = columnOptions ? keyOf("formatters") : void 0;
|
|
1769
|
+
const single = (kind) => entries.filter(([, spec]) => spec.kind === kind);
|
|
1770
|
+
const formatterKey = columnOptions && single("dimension").length === 1 && single("formatter").length === 1 ? single("formatter")[0]?.[0] : void 0;
|
|
1771
|
+
const labels = labelsKey && values[labelsKey] || {};
|
|
1772
|
+
const formats = formatsKey && values[formatsKey] || {};
|
|
1773
|
+
const dimensionFormatters = formattersKey && values[formattersKey] || {};
|
|
1774
|
+
const optionsButton = (kind, name) => {
|
|
1775
|
+
if (!columnOptions || !name) return null;
|
|
1776
|
+
const canFormatter = kind === "dimension" && !!(formattersKey || formatterKey);
|
|
1777
|
+
return /* @__PURE__ */ jsx(ColumnOptions, {
|
|
1778
|
+
kind,
|
|
1779
|
+
name,
|
|
1780
|
+
model,
|
|
1781
|
+
label: labelsKey ? labels[name] ?? "" : void 0,
|
|
1782
|
+
onLabel: (text) => labelsKey && set(labelsKey, pruned(withField(labels, name, text), columns)),
|
|
1783
|
+
format: kind === "measure" && formatsKey ? formats[name] ?? {} : void 0,
|
|
1784
|
+
onFormat: (format) => {
|
|
1785
|
+
if (!formatsKey) return;
|
|
1786
|
+
const map = { ...formats };
|
|
1787
|
+
if (Object.keys(format).length) map[name] = format;
|
|
1788
|
+
else delete map[name];
|
|
1789
|
+
set(formatsKey, pruned(map, chosenMeasures));
|
|
1790
|
+
},
|
|
1791
|
+
formatter: !canFormatter ? void 0 : formattersKey ? dimensionFormatters[name] ?? "" : (formatterKey && values[formatterKey]) ?? "",
|
|
1792
|
+
formatters,
|
|
1793
|
+
onFormatter: (formatter) => {
|
|
1794
|
+
if (formattersKey) set(formattersKey, pruned(withField(dimensionFormatters, name, formatter), chosenDimensions));
|
|
1795
|
+
else if (formatterKey) set(formatterKey, formatter || void 0);
|
|
1796
|
+
},
|
|
1797
|
+
onReset: () => {
|
|
1798
|
+
const changes = {};
|
|
1799
|
+
if (labelsKey) changes[labelsKey] = pruned(withField(labels, name, void 0), columns);
|
|
1800
|
+
if (formatsKey && kind === "measure") changes[formatsKey] = pruned(withField(formats, name, void 0), chosenMeasures);
|
|
1801
|
+
if (canFormatter && formattersKey) changes[formattersKey] = pruned(withField(dimensionFormatters, name, void 0), chosenDimensions);
|
|
1802
|
+
else if (canFormatter && formatterKey) changes[formatterKey] = void 0;
|
|
1803
|
+
if (onSetMany) onSetMany(changes);
|
|
1804
|
+
else for (const [key, value] of Object.entries(changes)) set(key, value);
|
|
1805
|
+
}
|
|
1806
|
+
});
|
|
1807
|
+
};
|
|
1808
|
+
const merged = (() => {
|
|
1809
|
+
for (const [dimensionsKey, spec] of entries) {
|
|
1810
|
+
if (spec.kind !== "dimensions") continue;
|
|
1811
|
+
const pair = entries.find(([, other]) => other.kind === "measures" && other.section === spec.section);
|
|
1812
|
+
if (!pair) continue;
|
|
1813
|
+
const order = entries.find(([, other]) => other.kind === "columnOrder")?.[0];
|
|
1814
|
+
return {
|
|
1815
|
+
dimensions: dimensionsKey,
|
|
1816
|
+
dimensionsSpec: spec,
|
|
1817
|
+
measures: pair[0],
|
|
1818
|
+
measuresSpec: pair[1],
|
|
1819
|
+
order
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
})();
|
|
1823
|
+
const mergedInput = (pair) => {
|
|
1824
|
+
const dimensionNames = values[pair.dimensions] ?? [];
|
|
1825
|
+
const measureNames = values[pair.measures] ?? [];
|
|
1826
|
+
const all = [...dimensionNames.map((name) => ({
|
|
1827
|
+
kind: "dimension",
|
|
1828
|
+
name
|
|
1829
|
+
})), ...measureNames.map((name) => ({
|
|
1830
|
+
kind: "measure",
|
|
1831
|
+
name
|
|
1832
|
+
}))];
|
|
1833
|
+
const order = pair.order ? values[pair.order] : void 0;
|
|
1834
|
+
const sortedNames = orderedColumns(all.map((item) => item.name), order);
|
|
1835
|
+
const pool = [...all];
|
|
1836
|
+
const items = sortedNames.map((name) => {
|
|
1837
|
+
const index = pool.findIndex((item) => item.name === name);
|
|
1838
|
+
return pool.splice(index, 1)[0];
|
|
1839
|
+
});
|
|
1840
|
+
const required = !!pair.dimensionsSpec.required || !!pair.measuresSpec.required;
|
|
1841
|
+
return /* @__PURE__ */ jsx(ColumnList, {
|
|
1842
|
+
items,
|
|
1843
|
+
options: {
|
|
1844
|
+
dimension: options(pair.dimensionsSpec),
|
|
1845
|
+
measure: options(pair.measuresSpec)
|
|
1846
|
+
},
|
|
1847
|
+
extra: (item) => optionsButton(item.kind, item.name),
|
|
1848
|
+
error: !!pair.measuresSpec.required && !measureNames.length || !!pair.dimensionsSpec.required && !dimensionNames.length || required && !items.length,
|
|
1849
|
+
onChange: (next) => {
|
|
1850
|
+
const dimensionList = next.filter((item) => item.kind === "dimension");
|
|
1851
|
+
const measureList = next.filter((item) => item.kind === "measure");
|
|
1852
|
+
const names = next.map((item) => item.name);
|
|
1853
|
+
const standard = [...dimensionList, ...measureList].map((item) => item.name);
|
|
1854
|
+
const changes = {
|
|
1855
|
+
[pair.dimensions]: dimensionList.length || pair.dimensionsSpec.required ? dimensionList.map((item) => item.name) : void 0,
|
|
1856
|
+
[pair.measures]: measureList.length || pair.measuresSpec.required ? measureList.map((item) => item.name) : void 0
|
|
1857
|
+
};
|
|
1858
|
+
if (pair.order) changes[pair.order] = names.join("\0") === standard.join("\0") ? void 0 : names;
|
|
1859
|
+
if (onSetMany) onSetMany(changes);
|
|
1860
|
+
else for (const [key, value] of Object.entries(changes)) set(key, value);
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
};
|
|
1717
1864
|
const options = (spec) => {
|
|
1718
1865
|
switch (spec.kind) {
|
|
1719
1866
|
case "dimension":
|
|
@@ -1793,12 +1940,24 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1793
1940
|
sx: inputSx$1
|
|
1794
1941
|
});
|
|
1795
1942
|
case "dimensions":
|
|
1796
|
-
case "measures":
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1943
|
+
case "measures": {
|
|
1944
|
+
const kind = spec.kind === "dimensions" ? "dimension" : "measure";
|
|
1945
|
+
if (merged?.dimensions === key) return mergedInput(merged);
|
|
1946
|
+
const names = value ?? [];
|
|
1947
|
+
return /* @__PURE__ */ jsx(ColumnList, {
|
|
1948
|
+
items: names.map((name) => ({
|
|
1949
|
+
kind,
|
|
1950
|
+
name
|
|
1951
|
+
})),
|
|
1952
|
+
options: { [kind]: options(spec) },
|
|
1953
|
+
extra: (item) => optionsButton(item.kind, item.name),
|
|
1954
|
+
error: !!spec.required && !names.length,
|
|
1955
|
+
onChange: (next) => {
|
|
1956
|
+
const list = next.map((item) => item.name);
|
|
1957
|
+
set(key, list.length || spec.required ? list : void 0);
|
|
1958
|
+
}
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1802
1961
|
case "formatters": {
|
|
1803
1962
|
const map = value ?? {};
|
|
1804
1963
|
if (!chosenDimensions.length) return /* @__PURE__ */ jsx(Typography, {
|
|
@@ -1838,45 +1997,37 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1838
1997
|
}, dimension))
|
|
1839
1998
|
});
|
|
1840
1999
|
}
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
},
|
|
1847
|
-
|
|
1848
|
-
});
|
|
1849
|
-
return /* @__PURE__ */ jsx(LabelsEditor, {
|
|
1850
|
-
value: value ?? {},
|
|
1851
|
-
columns,
|
|
1852
|
-
model,
|
|
1853
|
-
onChange: (next) => set(key, next)
|
|
2000
|
+
default: {
|
|
2001
|
+
const select = /* @__PURE__ */ jsx(NameSelect, {
|
|
2002
|
+
value: value ?? "",
|
|
2003
|
+
options: options(spec),
|
|
2004
|
+
required: spec.required,
|
|
2005
|
+
emptyLabel: spec.kind === "enum" && spec.default ? `padrão (${spec.labels?.[spec.default] ?? spec.default})` : void 0,
|
|
2006
|
+
onChange: (name) => set(key, name === spec.default ? void 0 : name)
|
|
1854
2007
|
});
|
|
1855
|
-
|
|
1856
|
-
|
|
2008
|
+
const button = spec.kind === "dimension" || spec.kind === "measure" ? optionsButton(spec.kind, value) : null;
|
|
2009
|
+
return button ? /* @__PURE__ */ jsxs(Box, {
|
|
1857
2010
|
sx: {
|
|
1858
|
-
|
|
1859
|
-
|
|
2011
|
+
display: "flex",
|
|
2012
|
+
gap: .25,
|
|
2013
|
+
alignItems: "center"
|
|
1860
2014
|
},
|
|
1861
|
-
children:
|
|
1862
|
-
});
|
|
1863
|
-
|
|
1864
|
-
value: value ?? {},
|
|
1865
|
-
measures: chosenMeasures,
|
|
1866
|
-
model,
|
|
1867
|
-
onChange: (next) => set(key, next)
|
|
1868
|
-
});
|
|
1869
|
-
default: return /* @__PURE__ */ jsx(NameSelect, {
|
|
1870
|
-
value: value ?? "",
|
|
1871
|
-
options: options(spec),
|
|
1872
|
-
required: spec.required,
|
|
1873
|
-
emptyLabel: spec.kind === "enum" && spec.default ? `padrão (${spec.labels?.[spec.default] ?? spec.default})` : void 0,
|
|
1874
|
-
onChange: (name) => set(key, name === spec.default ? void 0 : name)
|
|
1875
|
-
});
|
|
2015
|
+
children: [select, button]
|
|
2016
|
+
}) : select;
|
|
2017
|
+
}
|
|
1876
2018
|
}
|
|
1877
2019
|
};
|
|
1878
|
-
const
|
|
1879
|
-
|
|
2020
|
+
const hidden = new Set([
|
|
2021
|
+
merged?.measures,
|
|
2022
|
+
...entries.filter(([, spec]) => spec.kind === "columnOrder").map(([key]) => key),
|
|
2023
|
+
labelsKey,
|
|
2024
|
+
formatsKey,
|
|
2025
|
+
formattersKey,
|
|
2026
|
+
formatterKey
|
|
2027
|
+
].filter(Boolean));
|
|
2028
|
+
const sections = [...new Set(entries.filter(([key]) => !hidden.has(key)).map(([, spec]) => spec.section))];
|
|
2029
|
+
const fullRow = (spec) => spec.kind === "dimensions" || spec.kind === "measures" || spec.kind === "formatters";
|
|
2030
|
+
const gridColumn = (spec, key) => fullRow(spec) ? "1 / -1" : key === "title" && formColumns > 1 ? "span 2" : void 0;
|
|
1880
2031
|
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((section) => {
|
|
1881
2032
|
const inSection = entries.filter(([, spec]) => spec.section === section);
|
|
1882
2033
|
return /* @__PURE__ */ jsx(Section, {
|
|
@@ -1884,20 +2035,20 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1884
2035
|
collapseKey: `layout:form:${section}`,
|
|
1885
2036
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
1886
2037
|
sx: formGrid(formColumns),
|
|
1887
|
-
children: [section === sections[0] && header, inSection.map(([key, spec]) => /* @__PURE__ */ jsx(Box, {
|
|
2038
|
+
children: [section === sections[0] && header, inSection.filter(([key]) => !hidden.has(key)).map(([key, spec]) => /* @__PURE__ */ jsx(Box, {
|
|
1888
2039
|
sx: {
|
|
1889
|
-
gridColumn:
|
|
2040
|
+
gridColumn: gridColumn(spec, key),
|
|
1890
2041
|
alignSelf: spec.kind === "boolean" ? "end" : void 0
|
|
1891
2042
|
},
|
|
1892
|
-
children: spec.kind === "boolean" ? withHint(input(key, spec), spec.hint) : field(spec.label, input(key, spec), spec.hint)
|
|
2043
|
+
children: spec.kind === "boolean" ? withHint(input(key, spec), spec.hint) : merged?.dimensions === key ? field("Dimensões e medidas", input(key, spec), `${merged.order ? "Em qualquer ordem: é a ordem das colunas. " : ""}${[spec.hint, merged.measuresSpec.hint].filter(Boolean).join(" · ")}`) : field(spec.label, input(key, spec), spec.hint)
|
|
1893
2044
|
}, key))]
|
|
1894
2045
|
})
|
|
1895
2046
|
}, section);
|
|
1896
2047
|
}) });
|
|
1897
2048
|
}
|
|
1898
2049
|
/**
|
|
1899
|
-
* Cor com o seletor nativo. Mexer no seletor só muda a amostra: o
|
|
1900
|
-
* ele fecha — o `change` nativo; o `onChange` do React dispara a cada movimento. O texto
|
|
2050
|
+
* Cor com o seletor nativo. Mexer no seletor só muda a amostra: o valor vai para o editor quando
|
|
2051
|
+
* ele fecha — o `change` nativo; o `onChange` do React dispara a cada movimento. O texto entra no
|
|
1901
2052
|
* Enter ou ao sair do campo.
|
|
1902
2053
|
*/
|
|
1903
2054
|
function ColorInput({ value, fallback, onCommit }) {
|
|
@@ -1964,61 +2115,30 @@ function pruned(map, keep) {
|
|
|
1964
2115
|
const next = Object.fromEntries(Object.entries(map).filter(([key]) => keep.includes(key)));
|
|
1965
2116
|
return Object.keys(next).length ? next : void 0;
|
|
1966
2117
|
}
|
|
1967
|
-
const columnNameSx = {
|
|
1968
|
-
fontFamily: MONO,
|
|
1969
|
-
fontSize: 12,
|
|
1970
|
-
minWidth: 90,
|
|
1971
|
-
maxWidth: 140,
|
|
1972
|
-
overflow: "hidden",
|
|
1973
|
-
textOverflow: "ellipsis",
|
|
1974
|
-
whiteSpace: "nowrap"
|
|
1975
|
-
};
|
|
1976
|
-
/** Rótulo de cada coluna do gráfico; o placeholder é o rótulo do dicionário. */
|
|
1977
|
-
function LabelsEditor({ value, columns, model, onChange }) {
|
|
1978
|
-
return /* @__PURE__ */ jsx(Box, {
|
|
1979
|
-
sx: {
|
|
1980
|
-
display: "flex",
|
|
1981
|
-
flexDirection: "column",
|
|
1982
|
-
gap: .5
|
|
1983
|
-
},
|
|
1984
|
-
children: columns.map((column) => /* @__PURE__ */ jsxs(Box, {
|
|
1985
|
-
sx: {
|
|
1986
|
-
display: "flex",
|
|
1987
|
-
gap: .5,
|
|
1988
|
-
alignItems: "center"
|
|
1989
|
-
},
|
|
1990
|
-
children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
1991
|
-
title: model.dimensions[column] ? "dimensão" : "medida",
|
|
1992
|
-
placement: "left",
|
|
1993
|
-
children: /* @__PURE__ */ jsx(Typography, {
|
|
1994
|
-
sx: columnNameSx,
|
|
1995
|
-
children: column
|
|
1996
|
-
})
|
|
1997
|
-
}), /* @__PURE__ */ jsx(TextField, {
|
|
1998
|
-
size: "small",
|
|
1999
|
-
fullWidth: true,
|
|
2000
|
-
value: value[column] ?? "",
|
|
2001
|
-
placeholder: model.dimensions[column]?.label ?? model.measures[column]?.label ?? column,
|
|
2002
|
-
onChange: (e) => onChange(pruned(withField(value, column, e.target.value), columns)),
|
|
2003
|
-
sx: inputSx$1
|
|
2004
|
-
})]
|
|
2005
|
-
}, column))
|
|
2006
|
-
});
|
|
2007
|
-
}
|
|
2008
2118
|
const FORMAT_LABEL = {
|
|
2009
2119
|
number: "número",
|
|
2010
2120
|
integer: "inteiro",
|
|
2011
2121
|
currency: "R$",
|
|
2012
2122
|
percent: "%"
|
|
2013
2123
|
};
|
|
2014
|
-
/**
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2124
|
+
/** Número de exemplo da prévia do formato. */
|
|
2125
|
+
const SAMPLE = 1234567.891;
|
|
2126
|
+
/**
|
|
2127
|
+
* Ícone à direita de uma dimensão/medida que abre as opções dela no gráfico: rótulo, formatter
|
|
2128
|
+
* (dimensão) e formato (medida). Vazio = o do dicionário do modelo (que aparece como placeholder).
|
|
2129
|
+
* Opção ausente (`undefined`) = o tipo do gráfico não tem esse campo.
|
|
2130
|
+
*/
|
|
2131
|
+
function ColumnOptions({ kind, name, model, label, onLabel, format, onFormat, formatter, formatters, onFormatter, onReset }) {
|
|
2132
|
+
const [anchor, setAnchor] = useState(null);
|
|
2133
|
+
const fromModel = kind === "measure" ? model.measures[name] : model.dimensions[name];
|
|
2134
|
+
const measureMeta = kind === "measure" ? model.measures[name] : void 0;
|
|
2135
|
+
const customized = !!label || !!formatter || format !== void 0 && Object.keys(format).length > 0;
|
|
2136
|
+
const setFormat = (key, next) => onFormat(withField(format ?? {}, key, next));
|
|
2137
|
+
const decimals = format?.decimals;
|
|
2138
|
+
const resolved = {
|
|
2139
|
+
...measureMeta?.format !== void 0 && { format: measureMeta.format },
|
|
2140
|
+
...measureMeta?.decimals !== void 0 && { decimals: measureMeta.decimals },
|
|
2141
|
+
...format
|
|
2022
2142
|
};
|
|
2023
2143
|
const small = {
|
|
2024
2144
|
...inputSx$1,
|
|
@@ -2028,104 +2148,192 @@ function FormatsEditor({ value, measures, model, onChange }) {
|
|
|
2028
2148
|
px: 1
|
|
2029
2149
|
}
|
|
2030
2150
|
};
|
|
2031
|
-
return /* @__PURE__ */ jsx(
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2151
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tooltip, {
|
|
2152
|
+
title: customized ? "Opções da coluna (personalizada)" : "Opções da coluna",
|
|
2153
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
2154
|
+
size: "small",
|
|
2155
|
+
"aria-label": `Opções de ${name}`,
|
|
2156
|
+
onClick: (e) => setAnchor(e.currentTarget),
|
|
2157
|
+
sx: {
|
|
2158
|
+
p: .25,
|
|
2159
|
+
fontSize: 14,
|
|
2160
|
+
color: customized ? "primary.main" : "text.secondary"
|
|
2161
|
+
},
|
|
2162
|
+
children: "⚙"
|
|
2163
|
+
})
|
|
2164
|
+
}), /* @__PURE__ */ jsxs(Popover, {
|
|
2165
|
+
open: !!anchor,
|
|
2166
|
+
anchorEl: anchor,
|
|
2167
|
+
onClose: () => setAnchor(null),
|
|
2168
|
+
anchorOrigin: {
|
|
2169
|
+
vertical: "bottom",
|
|
2170
|
+
horizontal: "right"
|
|
2036
2171
|
},
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2172
|
+
transformOrigin: {
|
|
2173
|
+
vertical: "top",
|
|
2174
|
+
horizontal: "right"
|
|
2175
|
+
},
|
|
2176
|
+
slotProps: { paper: { sx: {
|
|
2177
|
+
p: 1.5,
|
|
2178
|
+
width: 320
|
|
2179
|
+
} } },
|
|
2180
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
2181
|
+
sx: {
|
|
2182
|
+
fontSize: 12,
|
|
2183
|
+
fontWeight: 700,
|
|
2184
|
+
mb: 1
|
|
2185
|
+
},
|
|
2186
|
+
children: [
|
|
2187
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2188
|
+
component: "span",
|
|
2189
|
+
sx: {
|
|
2190
|
+
color: KIND_STYLE[kind].color,
|
|
2191
|
+
mr: .75
|
|
2192
|
+
},
|
|
2193
|
+
children: KIND_STYLE[kind].icon
|
|
2194
|
+
}),
|
|
2195
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2196
|
+
component: "span",
|
|
2197
|
+
sx: { fontFamily: MONO },
|
|
2198
|
+
children: name
|
|
2199
|
+
}),
|
|
2200
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2048
2201
|
component: "span",
|
|
2049
2202
|
sx: {
|
|
2050
2203
|
color: "text.secondary",
|
|
2051
|
-
|
|
2052
|
-
|
|
2204
|
+
fontWeight: 400,
|
|
2205
|
+
ml: 1
|
|
2053
2206
|
},
|
|
2054
|
-
children:
|
|
2055
|
-
})
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2207
|
+
children: KIND_STYLE[kind].noun
|
|
2208
|
+
})
|
|
2209
|
+
]
|
|
2210
|
+
}), /* @__PURE__ */ jsxs(Box, {
|
|
2211
|
+
sx: {
|
|
2212
|
+
display: "flex",
|
|
2213
|
+
flexDirection: "column",
|
|
2214
|
+
gap: 1.25
|
|
2215
|
+
},
|
|
2216
|
+
children: [
|
|
2217
|
+
label !== void 0 && field("Rótulo", /* @__PURE__ */ jsx(TextField, {
|
|
2218
|
+
size: "small",
|
|
2219
|
+
fullWidth: true,
|
|
2220
|
+
autoFocus: true,
|
|
2221
|
+
value: label,
|
|
2222
|
+
placeholder: fromModel?.label ?? name,
|
|
2223
|
+
onChange: (e) => onLabel(e.target.value),
|
|
2224
|
+
sx: inputSx$1
|
|
2225
|
+
}), "Legenda, tooltip e cabeçalho, só neste gráfico; vazio = rótulo do dicionário"),
|
|
2226
|
+
formatter !== void 0 && field("Formatter", /* @__PURE__ */ jsx(NameSelect, {
|
|
2227
|
+
value: formatter,
|
|
2228
|
+
options: formatters.map((option) => ({ name: option })),
|
|
2229
|
+
onChange: onFormatter
|
|
2230
|
+
}), "Formatters passados ao <DashboardCharts formatters={{ ... }}> da página"),
|
|
2231
|
+
format !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2232
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2233
|
+
sx: {
|
|
2234
|
+
display: "grid",
|
|
2235
|
+
gridTemplateColumns: "1fr 1fr",
|
|
2236
|
+
gap: 1
|
|
2237
|
+
},
|
|
2238
|
+
children: [
|
|
2239
|
+
field("Formato", /* @__PURE__ */ jsxs(TextField, {
|
|
2240
|
+
select: true,
|
|
2241
|
+
size: "small",
|
|
2242
|
+
fullWidth: true,
|
|
2243
|
+
value: format.format ?? "",
|
|
2244
|
+
onChange: (e) => setFormat("format", e.target.value || void 0),
|
|
2245
|
+
slotProps: { select: { displayEmpty: true } },
|
|
2246
|
+
sx: small,
|
|
2247
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2248
|
+
value: "",
|
|
2249
|
+
children: /* @__PURE__ */ jsxs("em", { children: ["padrão", measureMeta?.format ? ` (${FORMAT_LABEL[measureMeta.format]})` : ""] })
|
|
2250
|
+
}), VALUE_FORMATS.map((option) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2251
|
+
value: option,
|
|
2252
|
+
children: FORMAT_LABEL[option]
|
|
2253
|
+
}, option))]
|
|
2254
|
+
})),
|
|
2255
|
+
field("Casas decimais", /* @__PURE__ */ jsx(TextField, {
|
|
2256
|
+
size: "small",
|
|
2257
|
+
fullWidth: true,
|
|
2258
|
+
type: "number",
|
|
2259
|
+
value: decimals ?? "",
|
|
2260
|
+
placeholder: measureMeta?.decimals === void 0 ? "padrão" : String(measureMeta.decimals),
|
|
2261
|
+
error: decimals !== void 0 && (!Number.isInteger(decimals) || decimals < 0 || decimals > 10),
|
|
2262
|
+
onChange: (e) => setFormat("decimals", e.target.value.trim() === "" ? void 0 : Number(e.target.value)),
|
|
2263
|
+
slotProps: { htmlInput: {
|
|
2264
|
+
min: 0,
|
|
2265
|
+
max: 10
|
|
2266
|
+
} },
|
|
2267
|
+
sx: small
|
|
2268
|
+
})),
|
|
2269
|
+
field("Prefixo", /* @__PURE__ */ jsx(TextField, {
|
|
2270
|
+
size: "small",
|
|
2271
|
+
fullWidth: true,
|
|
2272
|
+
value: format.prefix ?? "",
|
|
2273
|
+
placeholder: "ex.: ~",
|
|
2274
|
+
onChange: (e) => setFormat("prefix", e.target.value),
|
|
2275
|
+
sx: small
|
|
2276
|
+
})),
|
|
2277
|
+
field("Sufixo", /* @__PURE__ */ jsx(TextField, {
|
|
2278
|
+
size: "small",
|
|
2279
|
+
fullWidth: true,
|
|
2280
|
+
value: format.suffix ?? "",
|
|
2281
|
+
placeholder: "ex.: t",
|
|
2282
|
+
onChange: (e) => setFormat("suffix", e.target.value),
|
|
2283
|
+
sx: small
|
|
2284
|
+
}))
|
|
2285
|
+
]
|
|
2078
2286
|
}),
|
|
2079
|
-
/* @__PURE__ */ jsx(
|
|
2080
|
-
|
|
2081
|
-
|
|
2287
|
+
/* @__PURE__ */ jsx(FormControlLabel, {
|
|
2288
|
+
sx: { m: 0 },
|
|
2289
|
+
control: /* @__PURE__ */ jsx(Checkbox, {
|
|
2082
2290
|
size: "small",
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2291
|
+
sx: {
|
|
2292
|
+
p: .25,
|
|
2293
|
+
mr: .5
|
|
2294
|
+
},
|
|
2295
|
+
checked: format.compact ?? false,
|
|
2296
|
+
onChange: (e) => setFormat("compact", e.target.checked || void 0)
|
|
2297
|
+
}),
|
|
2298
|
+
label: /* @__PURE__ */ jsx(Typography, {
|
|
2299
|
+
sx: { fontSize: 12 },
|
|
2300
|
+
children: "Notação curta (1,2 mil / 3,4 mi)"
|
|
2093
2301
|
})
|
|
2094
2302
|
}),
|
|
2095
|
-
/* @__PURE__ */
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
title: "Notação curta: 1,2 mil / 3,4 mi",
|
|
2111
|
-
children: /* @__PURE__ */ jsx(FormControlLabel, {
|
|
2112
|
-
sx: { m: 0 },
|
|
2113
|
-
control: /* @__PURE__ */ jsx(Checkbox, {
|
|
2114
|
-
size: "small",
|
|
2115
|
-
sx: { p: .25 },
|
|
2116
|
-
checked: format.compact ?? false,
|
|
2117
|
-
onChange: (e) => setFormat(measure, "compact", e.target.checked || void 0)
|
|
2118
|
-
}),
|
|
2119
|
-
label: /* @__PURE__ */ jsx(Typography, {
|
|
2120
|
-
sx: { fontSize: 11 },
|
|
2121
|
-
children: "curto"
|
|
2303
|
+
/* @__PURE__ */ jsxs(Typography, {
|
|
2304
|
+
sx: {
|
|
2305
|
+
fontSize: 11,
|
|
2306
|
+
color: "text.secondary"
|
|
2307
|
+
},
|
|
2308
|
+
children: [
|
|
2309
|
+
"Exemplo:",
|
|
2310
|
+
" ",
|
|
2311
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2312
|
+
component: "span",
|
|
2313
|
+
sx: {
|
|
2314
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
2315
|
+
color: "text.primary"
|
|
2316
|
+
},
|
|
2317
|
+
children: formatMeasure(SAMPLE, resolved, 2)
|
|
2122
2318
|
})
|
|
2123
|
-
|
|
2319
|
+
]
|
|
2124
2320
|
})
|
|
2125
|
-
]
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2321
|
+
] }),
|
|
2322
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2323
|
+
sx: {
|
|
2324
|
+
display: "flex",
|
|
2325
|
+
justifyContent: "flex-end"
|
|
2326
|
+
},
|
|
2327
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
2328
|
+
size: "small",
|
|
2329
|
+
disabled: !customized,
|
|
2330
|
+
onClick: onReset,
|
|
2331
|
+
children: "Restaurar padrão"
|
|
2332
|
+
})
|
|
2333
|
+
})
|
|
2334
|
+
]
|
|
2335
|
+
})]
|
|
2336
|
+
})] });
|
|
2129
2337
|
}
|
|
2130
2338
|
const formGrid = (columns) => ({
|
|
2131
2339
|
display: "grid",
|
|
@@ -2199,52 +2407,205 @@ function NameSelect({ value, options, required, emptyLabel, onChange }) {
|
|
|
2199
2407
|
]
|
|
2200
2408
|
});
|
|
2201
2409
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2410
|
+
const KIND_STYLE = {
|
|
2411
|
+
dimension: {
|
|
2412
|
+
icon: "◇",
|
|
2413
|
+
color: "#4dabf7",
|
|
2414
|
+
noun: "dimensão",
|
|
2415
|
+
group: "Dimensões"
|
|
2416
|
+
},
|
|
2417
|
+
measure: {
|
|
2418
|
+
icon: "Σ",
|
|
2419
|
+
color: "#F58220",
|
|
2420
|
+
noun: "medida",
|
|
2421
|
+
group: "Medidas"
|
|
2422
|
+
}
|
|
2423
|
+
};
|
|
2424
|
+
/**
|
|
2425
|
+
* Lista ordenada, uma linha por item: a ordem é a das séries/colunas. Reordena arrastando pela alça
|
|
2426
|
+
* (⋮⋮) ou com ↑/↓; um único "+ adicionar" no fim (já com o nome escolhido, sem linha vazia). Com
|
|
2427
|
+
* dois tipos (dimensões e medidas juntas), o adicionar agrupa as opções por tipo e a ordem mistura
|
|
2428
|
+
* os dois.
|
|
2429
|
+
*/
|
|
2430
|
+
function ColumnList({ items, options, extra, error, onChange }) {
|
|
2431
|
+
const [drag, setDrag] = useState(null);
|
|
2432
|
+
const [grabbed, setGrabbed] = useState(null);
|
|
2433
|
+
const kinds = Object.keys(options);
|
|
2434
|
+
const mixed = kinds.length > 1;
|
|
2435
|
+
const used = (kind, name) => items.some((item) => item.kind === kind && item.name === name);
|
|
2436
|
+
const free = (kind, keep) => (options[kind] ?? []).filter((option) => option.name === keep || !used(kind, option.name));
|
|
2437
|
+
const replace = (index, name) => onChange(items.map((item, i) => i === index ? {
|
|
2438
|
+
...item,
|
|
2439
|
+
name
|
|
2440
|
+
} : item));
|
|
2441
|
+
const marker = {
|
|
2442
|
+
content: "\"\"",
|
|
2443
|
+
position: "absolute",
|
|
2444
|
+
left: 0,
|
|
2445
|
+
right: 0,
|
|
2446
|
+
height: 2,
|
|
2447
|
+
bgcolor: "primary.main"
|
|
2448
|
+
};
|
|
2449
|
+
const addable = kinds.filter((kind) => free(kind).length);
|
|
2450
|
+
const addOptions = addable.flatMap((kind) => [...mixed ? [/* @__PURE__ */ jsx(ListSubheader, { children: KIND_STYLE[kind].group }, `h:${kind}`)] : [], ...free(kind).map((option) => /* @__PURE__ */ jsxs(MenuItem, {
|
|
2451
|
+
value: `${kind}:${option.name}`,
|
|
2452
|
+
children: [mixed && /* @__PURE__ */ jsx(Box, {
|
|
2453
|
+
component: "span",
|
|
2454
|
+
sx: {
|
|
2455
|
+
color: KIND_STYLE[kind].color,
|
|
2456
|
+
mr: .75
|
|
2457
|
+
},
|
|
2458
|
+
children: KIND_STYLE[kind].icon
|
|
2459
|
+
}), /* @__PURE__ */ jsx(OptionText, { option })]
|
|
2460
|
+
}, `${kind}:${option.name}`))]);
|
|
2461
|
+
const addNoun = mixed ? kinds.map((kind) => KIND_STYLE[kind].noun).join(" ou ") : KIND_STYLE[kinds[0] ?? "measure"].noun;
|
|
2462
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2463
|
+
sx: {
|
|
2464
|
+
display: "flex",
|
|
2465
|
+
flexDirection: "column",
|
|
2466
|
+
gap: .5,
|
|
2467
|
+
maxWidth: 760
|
|
2214
2468
|
},
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2469
|
+
children: [items.map((item, index) => /* @__PURE__ */ jsxs(Box, {
|
|
2470
|
+
draggable: grabbed === index,
|
|
2471
|
+
onDragStart: (e) => {
|
|
2472
|
+
e.dataTransfer.effectAllowed = "move";
|
|
2473
|
+
e.dataTransfer.setData("text/plain", item.name);
|
|
2474
|
+
setDrag({
|
|
2475
|
+
from: index,
|
|
2476
|
+
to: index
|
|
2477
|
+
});
|
|
2478
|
+
},
|
|
2479
|
+
onDragOver: (e) => {
|
|
2480
|
+
if (!drag) return;
|
|
2481
|
+
e.preventDefault();
|
|
2482
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
2483
|
+
const to = e.clientY < rect.top + rect.height / 2 ? index : index + 1;
|
|
2484
|
+
if (to !== drag.to) setDrag({
|
|
2485
|
+
...drag,
|
|
2486
|
+
to
|
|
2487
|
+
});
|
|
2488
|
+
},
|
|
2489
|
+
onDrop: (e) => {
|
|
2490
|
+
e.preventDefault();
|
|
2491
|
+
if (drag && drag.to !== drag.from && drag.to !== drag.from + 1) onChange(moveTo(items, drag.from, drag.to));
|
|
2492
|
+
setDrag(null);
|
|
2493
|
+
},
|
|
2494
|
+
onDragEnd: () => {
|
|
2495
|
+
setDrag(null);
|
|
2496
|
+
setGrabbed(null);
|
|
2497
|
+
},
|
|
2498
|
+
sx: {
|
|
2499
|
+
position: "relative",
|
|
2500
|
+
display: "flex",
|
|
2501
|
+
gap: .25,
|
|
2502
|
+
alignItems: "center",
|
|
2503
|
+
opacity: drag?.from === index ? .4 : 1,
|
|
2504
|
+
"&:hover .row-actions": { visibility: "visible" },
|
|
2505
|
+
"&::before": drag && drag.from !== index && drag.to === index ? {
|
|
2506
|
+
...marker,
|
|
2507
|
+
top: -3
|
|
2508
|
+
} : void 0,
|
|
2509
|
+
"&::after": drag && index === items.length - 1 && drag.to === items.length ? {
|
|
2510
|
+
...marker,
|
|
2511
|
+
bottom: -3
|
|
2512
|
+
} : void 0
|
|
2513
|
+
},
|
|
2227
2514
|
children: [
|
|
2228
|
-
/* @__PURE__ */ jsx(
|
|
2229
|
-
|
|
2230
|
-
|
|
2515
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2516
|
+
component: "span",
|
|
2517
|
+
title: "Arraste para reordenar",
|
|
2518
|
+
onMouseDown: () => setGrabbed(index),
|
|
2519
|
+
onMouseUp: () => setGrabbed(null),
|
|
2231
2520
|
sx: {
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2521
|
+
color: "text.disabled",
|
|
2522
|
+
fontSize: 12,
|
|
2523
|
+
cursor: "grab",
|
|
2524
|
+
userSelect: "none",
|
|
2525
|
+
px: .25
|
|
2526
|
+
},
|
|
2527
|
+
children: "⋮⋮"
|
|
2235
2528
|
}),
|
|
2236
|
-
|
|
2237
|
-
component: "span",
|
|
2529
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
2238
2530
|
sx: {
|
|
2531
|
+
fontFamily: MONO,
|
|
2532
|
+
fontSize: 11,
|
|
2239
2533
|
color: "primary.main",
|
|
2240
|
-
|
|
2241
|
-
|
|
2534
|
+
minWidth: 14
|
|
2535
|
+
},
|
|
2536
|
+
children: index + 1
|
|
2537
|
+
}),
|
|
2538
|
+
mixed && /* @__PURE__ */ jsx(Tooltip, {
|
|
2539
|
+
title: KIND_STYLE[item.kind].noun,
|
|
2540
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
2541
|
+
component: "span",
|
|
2542
|
+
sx: {
|
|
2543
|
+
color: KIND_STYLE[item.kind].color,
|
|
2544
|
+
fontSize: 12,
|
|
2545
|
+
minWidth: 12
|
|
2546
|
+
},
|
|
2547
|
+
children: KIND_STYLE[item.kind].icon
|
|
2548
|
+
})
|
|
2549
|
+
}),
|
|
2550
|
+
/* @__PURE__ */ jsx(NameSelect, {
|
|
2551
|
+
value: item.name,
|
|
2552
|
+
options: free(item.kind, item.name),
|
|
2553
|
+
required: true,
|
|
2554
|
+
onChange: (next) => replace(index, next)
|
|
2555
|
+
}),
|
|
2556
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2557
|
+
className: "row-actions",
|
|
2558
|
+
sx: {
|
|
2559
|
+
display: "flex",
|
|
2560
|
+
visibility: "hidden"
|
|
2242
2561
|
},
|
|
2243
|
-
children:
|
|
2562
|
+
children: [
|
|
2563
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2564
|
+
title: "Subir",
|
|
2565
|
+
disabled: index === 0,
|
|
2566
|
+
onClick: () => onChange(moveTo(items, index, index - 1)),
|
|
2567
|
+
children: "↑"
|
|
2568
|
+
}),
|
|
2569
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2570
|
+
title: "Descer",
|
|
2571
|
+
disabled: index === items.length - 1,
|
|
2572
|
+
onClick: () => onChange(moveTo(items, index, index + 2)),
|
|
2573
|
+
children: "↓"
|
|
2574
|
+
}),
|
|
2575
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2576
|
+
title: "Remover",
|
|
2577
|
+
onClick: () => onChange(items.filter((_, i) => i !== index)),
|
|
2578
|
+
children: "×"
|
|
2579
|
+
})
|
|
2580
|
+
]
|
|
2244
2581
|
}),
|
|
2245
|
-
|
|
2582
|
+
extra?.(item)
|
|
2246
2583
|
]
|
|
2247
|
-
},
|
|
2584
|
+
}, `${item.kind}:${item.name}`)), addable.length > 0 && /* @__PURE__ */ jsx(TextField, {
|
|
2585
|
+
select: true,
|
|
2586
|
+
size: "small",
|
|
2587
|
+
fullWidth: true,
|
|
2588
|
+
value: "",
|
|
2589
|
+
error,
|
|
2590
|
+
onChange: (e) => {
|
|
2591
|
+
const [kind, ...rest] = e.target.value.split(":");
|
|
2592
|
+
const name = rest.join(":");
|
|
2593
|
+
if (name) onChange([...items, {
|
|
2594
|
+
kind,
|
|
2595
|
+
name
|
|
2596
|
+
}]);
|
|
2597
|
+
},
|
|
2598
|
+
slotProps: { select: {
|
|
2599
|
+
displayEmpty: true,
|
|
2600
|
+
renderValue: () => /* @__PURE__ */ jsxs(Box, {
|
|
2601
|
+
component: "span",
|
|
2602
|
+
sx: { color: "text.secondary" },
|
|
2603
|
+
children: ["+ adicionar ", addNoun]
|
|
2604
|
+
})
|
|
2605
|
+
} },
|
|
2606
|
+
sx: inputSx$1,
|
|
2607
|
+
children: addOptions
|
|
2608
|
+
})]
|
|
2248
2609
|
});
|
|
2249
2610
|
}
|
|
2250
2611
|
//#endregion
|
|
@@ -7270,4 +7631,4 @@ function ToggleButton$1({ draft, loading, problems, inspecting, onOpen }) {
|
|
|
7270
7631
|
//#endregion
|
|
7271
7632
|
export { DashboardDevtools as default };
|
|
7272
7633
|
|
|
7273
|
-
//# sourceMappingURL=DashboardDevtools-
|
|
7634
|
+
//# sourceMappingURL=DashboardDevtools-6v5HDe-A.js.map
|