@bluemarble/bi 0.3.1 → 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-C4E9afPT.js → DashboardDevtools-6v5HDe-A.js} +1046 -298
- package/dist/DashboardDevtools-6v5HDe-A.js.map +1 -0
- package/dist/{ModelDictionary-kUKcdbc8.js → ModelDictionary-D3JzCd6x.js} +81 -5
- package/dist/ModelDictionary-D3JzCd6x.js.map +1 -0
- package/dist/client/index.d.ts +62 -17
- package/dist/client/index.js +87 -90
- package/dist/client/index.js.map +1 -1
- package/dist/{layout-DTR4vI-I.js → layout-QuOYFmp1.js} +111 -35
- package/dist/layout-QuOYFmp1.js.map +1 -0
- package/dist/server/index.d.ts +1 -5
- package/dist/server/index.js +1 -1
- package/dist/{types-DUJS2Zmx.d.ts → types-FdrY0FOZ.d.ts} +59 -13
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/dist/DashboardDevtools-C4E9afPT.js.map +0 -1
- package/dist/ModelDictionary-kUKcdbc8.js.map +0 -1
- package/dist/layout-DTR4vI-I.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,16 +1691,19 @@ 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),
|
|
1657
1704
|
formColumns: columns,
|
|
1658
1705
|
chosenDimensions: chartReferences(chart, types).dimensions,
|
|
1706
|
+
chosenMeasures: chartReferences(chart, types).measures,
|
|
1659
1707
|
header: /* @__PURE__ */ jsxs(Fragment, { children: [field("Tipo", /* @__PURE__ */ jsxs(TextField, {
|
|
1660
1708
|
select: true,
|
|
1661
1709
|
size: "small",
|
|
@@ -1703,7 +1751,7 @@ function FilterForm({ filter, model, formatters, columns, onChange }) {
|
|
|
1703
1751
|
});
|
|
1704
1752
|
}
|
|
1705
1753
|
/** Formulário gerado dos `FieldSpec` (campos do tipo de gráfico ou do filtro). */
|
|
1706
|
-
function FieldsForm({ fields, values, onSet: set, model, formatters, columns, formColumns, chosenDimensions, header }) {
|
|
1754
|
+
function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters, columns, formColumns, chosenDimensions, chosenMeasures = [], header }) {
|
|
1707
1755
|
const dimensions = Object.entries(model.dimensions).map(([name, dim]) => ({
|
|
1708
1756
|
name,
|
|
1709
1757
|
label: dim.label
|
|
@@ -1713,6 +1761,106 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1713
1761
|
label: measure.label
|
|
1714
1762
|
}));
|
|
1715
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
|
+
};
|
|
1716
1864
|
const options = (spec) => {
|
|
1717
1865
|
switch (spec.kind) {
|
|
1718
1866
|
case "dimension":
|
|
@@ -1742,37 +1890,11 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1742
1890
|
onChange: (e) => set(key, e.target.value),
|
|
1743
1891
|
sx: inputSx$1
|
|
1744
1892
|
});
|
|
1745
|
-
case "color": {
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
gap: .5,
|
|
1751
|
-
alignItems: "center"
|
|
1752
|
-
},
|
|
1753
|
-
children: [/* @__PURE__ */ jsx(Box, {
|
|
1754
|
-
component: "input",
|
|
1755
|
-
type: "color",
|
|
1756
|
-
value: value ?? fallback,
|
|
1757
|
-
onChange: (e) => set(key, e.target.value),
|
|
1758
|
-
sx: {
|
|
1759
|
-
width: 32,
|
|
1760
|
-
height: 30,
|
|
1761
|
-
p: 0,
|
|
1762
|
-
border: 0,
|
|
1763
|
-
bgcolor: "transparent",
|
|
1764
|
-
cursor: "pointer"
|
|
1765
|
-
}
|
|
1766
|
-
}), /* @__PURE__ */ jsx(TextField, {
|
|
1767
|
-
size: "small",
|
|
1768
|
-
fullWidth: true,
|
|
1769
|
-
value: value ?? "",
|
|
1770
|
-
placeholder: fallback,
|
|
1771
|
-
onChange: (e) => set(key, e.target.value),
|
|
1772
|
-
sx: inputSx$1
|
|
1773
|
-
})]
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1893
|
+
case "color": return /* @__PURE__ */ jsx(ColorInput, {
|
|
1894
|
+
value,
|
|
1895
|
+
fallback: spec.default,
|
|
1896
|
+
onCommit: (color) => set(key, color)
|
|
1897
|
+
});
|
|
1776
1898
|
case "boolean": {
|
|
1777
1899
|
const fallback = spec.default;
|
|
1778
1900
|
return /* @__PURE__ */ jsx(FormControlLabel, {
|
|
@@ -1818,12 +1940,24 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1818
1940
|
sx: inputSx$1
|
|
1819
1941
|
});
|
|
1820
1942
|
case "dimensions":
|
|
1821
|
-
case "measures":
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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
|
+
}
|
|
1827
1961
|
case "formatters": {
|
|
1828
1962
|
const map = value ?? {};
|
|
1829
1963
|
if (!chosenDimensions.length) return /* @__PURE__ */ jsx(Typography, {
|
|
@@ -1863,17 +1997,37 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1863
1997
|
}, dimension))
|
|
1864
1998
|
});
|
|
1865
1999
|
}
|
|
1866
|
-
default:
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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)
|
|
2007
|
+
});
|
|
2008
|
+
const button = spec.kind === "dimension" || spec.kind === "measure" ? optionsButton(spec.kind, value) : null;
|
|
2009
|
+
return button ? /* @__PURE__ */ jsxs(Box, {
|
|
2010
|
+
sx: {
|
|
2011
|
+
display: "flex",
|
|
2012
|
+
gap: .25,
|
|
2013
|
+
alignItems: "center"
|
|
2014
|
+
},
|
|
2015
|
+
children: [select, button]
|
|
2016
|
+
}) : select;
|
|
2017
|
+
}
|
|
1873
2018
|
}
|
|
1874
2019
|
};
|
|
1875
|
-
const
|
|
1876
|
-
|
|
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;
|
|
1877
2031
|
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((section) => {
|
|
1878
2032
|
const inSection = entries.filter(([, spec]) => spec.section === section);
|
|
1879
2033
|
return /* @__PURE__ */ jsx(Section, {
|
|
@@ -1881,28 +2035,314 @@ function FieldsForm({ fields, values, onSet: set, model, formatters, columns, fo
|
|
|
1881
2035
|
collapseKey: `layout:form:${section}`,
|
|
1882
2036
|
children: /* @__PURE__ */ jsxs(Box, {
|
|
1883
2037
|
sx: formGrid(formColumns),
|
|
1884
|
-
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, {
|
|
1885
2039
|
sx: {
|
|
1886
|
-
gridColumn:
|
|
2040
|
+
gridColumn: gridColumn(spec, key),
|
|
1887
2041
|
alignSelf: spec.kind === "boolean" ? "end" : void 0
|
|
1888
2042
|
},
|
|
1889
|
-
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)
|
|
1890
2044
|
}, key))]
|
|
1891
2045
|
})
|
|
1892
2046
|
}, section);
|
|
1893
2047
|
}) });
|
|
1894
2048
|
}
|
|
2049
|
+
/**
|
|
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
|
|
2052
|
+
* Enter ou ao sair do campo.
|
|
2053
|
+
*/
|
|
2054
|
+
function ColorInput({ value, fallback, onCommit }) {
|
|
2055
|
+
const [picking, setPicking] = useState(null);
|
|
2056
|
+
const [typing, setTyping] = useState(null);
|
|
2057
|
+
const picker = useRef(null);
|
|
2058
|
+
const commit = useRef(onCommit);
|
|
2059
|
+
useEffect(() => {
|
|
2060
|
+
commit.current = onCommit;
|
|
2061
|
+
});
|
|
2062
|
+
useEffect(() => {
|
|
2063
|
+
const input = picker.current;
|
|
2064
|
+
if (!input) return;
|
|
2065
|
+
const listener = () => {
|
|
2066
|
+
commit.current(input.value);
|
|
2067
|
+
setPicking(null);
|
|
2068
|
+
};
|
|
2069
|
+
input.addEventListener("change", listener);
|
|
2070
|
+
return () => input.removeEventListener("change", listener);
|
|
2071
|
+
}, []);
|
|
2072
|
+
const commitText = () => {
|
|
2073
|
+
if (typing === null) return;
|
|
2074
|
+
const text = typing.trim();
|
|
2075
|
+
setTyping(null);
|
|
2076
|
+
if (text !== (value ?? "")) onCommit(text || void 0);
|
|
2077
|
+
};
|
|
2078
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2079
|
+
sx: {
|
|
2080
|
+
display: "flex",
|
|
2081
|
+
gap: .5,
|
|
2082
|
+
alignItems: "center"
|
|
2083
|
+
},
|
|
2084
|
+
children: [/* @__PURE__ */ jsx(Box, {
|
|
2085
|
+
component: "input",
|
|
2086
|
+
type: "color",
|
|
2087
|
+
ref: picker,
|
|
2088
|
+
value: picking ?? value ?? fallback,
|
|
2089
|
+
onChange: (e) => setPicking(e.target.value),
|
|
2090
|
+
sx: {
|
|
2091
|
+
width: 32,
|
|
2092
|
+
height: 30,
|
|
2093
|
+
p: 0,
|
|
2094
|
+
border: 0,
|
|
2095
|
+
bgcolor: "transparent",
|
|
2096
|
+
cursor: "pointer"
|
|
2097
|
+
}
|
|
2098
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
2099
|
+
size: "small",
|
|
2100
|
+
fullWidth: true,
|
|
2101
|
+
value: typing ?? picking ?? value ?? "",
|
|
2102
|
+
placeholder: fallback,
|
|
2103
|
+
onChange: (e) => setTyping(e.target.value),
|
|
2104
|
+
onBlur: commitText,
|
|
2105
|
+
onKeyDown: (e) => {
|
|
2106
|
+
if (e.key === "Enter") commitText();
|
|
2107
|
+
if (e.key === "Escape") setTyping(null);
|
|
2108
|
+
},
|
|
2109
|
+
sx: inputSx$1
|
|
2110
|
+
})]
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
/** Sem as chaves de colunas que saíram do gráfico; vazio = `undefined` (sai do JSON). */
|
|
2114
|
+
function pruned(map, keep) {
|
|
2115
|
+
const next = Object.fromEntries(Object.entries(map).filter(([key]) => keep.includes(key)));
|
|
2116
|
+
return Object.keys(next).length ? next : void 0;
|
|
2117
|
+
}
|
|
2118
|
+
const FORMAT_LABEL = {
|
|
2119
|
+
number: "número",
|
|
2120
|
+
integer: "inteiro",
|
|
2121
|
+
currency: "R$",
|
|
2122
|
+
percent: "%"
|
|
2123
|
+
};
|
|
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
|
|
2142
|
+
};
|
|
2143
|
+
const small = {
|
|
2144
|
+
...inputSx$1,
|
|
2145
|
+
"& .MuiInputBase-input": {
|
|
2146
|
+
fontFamily: MONO,
|
|
2147
|
+
fontSize: 12,
|
|
2148
|
+
px: 1
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
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"
|
|
2171
|
+
},
|
|
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, {
|
|
2201
|
+
component: "span",
|
|
2202
|
+
sx: {
|
|
2203
|
+
color: "text.secondary",
|
|
2204
|
+
fontWeight: 400,
|
|
2205
|
+
ml: 1
|
|
2206
|
+
},
|
|
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
|
+
]
|
|
2286
|
+
}),
|
|
2287
|
+
/* @__PURE__ */ jsx(FormControlLabel, {
|
|
2288
|
+
sx: { m: 0 },
|
|
2289
|
+
control: /* @__PURE__ */ jsx(Checkbox, {
|
|
2290
|
+
size: "small",
|
|
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)"
|
|
2301
|
+
})
|
|
2302
|
+
}),
|
|
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)
|
|
2318
|
+
})
|
|
2319
|
+
]
|
|
2320
|
+
})
|
|
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
|
+
})] });
|
|
2337
|
+
}
|
|
1895
2338
|
const formGrid = (columns) => ({
|
|
1896
2339
|
display: "grid",
|
|
1897
2340
|
gap: 1.5,
|
|
1898
2341
|
gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`
|
|
1899
2342
|
});
|
|
1900
|
-
/** Rótulo padrão (do dicionário) como placeholder
|
|
2343
|
+
/** Rótulo padrão (do dicionário) como placeholder do `label` do filtro. */
|
|
1901
2344
|
function labelPlaceholder(key, values, model) {
|
|
1902
2345
|
if (key === "label" && typeof values.dimension === "string") return model.dimensions[values.dimension]?.label ?? values.dimension;
|
|
1903
|
-
const measureKey = key.endsWith("Label") ? key.slice(0, -5) : null;
|
|
1904
|
-
const measure = measureKey && values[measureKey];
|
|
1905
|
-
return typeof measure === "string" ? model.measures[measure]?.label ?? measure : void 0;
|
|
1906
2346
|
}
|
|
1907
2347
|
function field(label, input, hint) {
|
|
1908
2348
|
return /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Typography, {
|
|
@@ -1967,52 +2407,205 @@ function NameSelect({ value, options, required, emptyLabel, onChange }) {
|
|
|
1967
2407
|
]
|
|
1968
2408
|
});
|
|
1969
2409
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
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
|
|
1982
2468
|
},
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
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
|
+
},
|
|
1995
2514
|
children: [
|
|
1996
|
-
/* @__PURE__ */ jsx(
|
|
1997
|
-
|
|
1998
|
-
|
|
2515
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2516
|
+
component: "span",
|
|
2517
|
+
title: "Arraste para reordenar",
|
|
2518
|
+
onMouseDown: () => setGrabbed(index),
|
|
2519
|
+
onMouseUp: () => setGrabbed(null),
|
|
1999
2520
|
sx: {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2521
|
+
color: "text.disabled",
|
|
2522
|
+
fontSize: 12,
|
|
2523
|
+
cursor: "grab",
|
|
2524
|
+
userSelect: "none",
|
|
2525
|
+
px: .25
|
|
2526
|
+
},
|
|
2527
|
+
children: "⋮⋮"
|
|
2003
2528
|
}),
|
|
2004
|
-
|
|
2005
|
-
component: "span",
|
|
2529
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
2006
2530
|
sx: {
|
|
2531
|
+
fontFamily: MONO,
|
|
2532
|
+
fontSize: 11,
|
|
2007
2533
|
color: "primary.main",
|
|
2008
|
-
|
|
2009
|
-
|
|
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"
|
|
2010
2561
|
},
|
|
2011
|
-
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
|
+
]
|
|
2012
2581
|
}),
|
|
2013
|
-
|
|
2582
|
+
extra?.(item)
|
|
2014
2583
|
]
|
|
2015
|
-
},
|
|
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
|
+
})]
|
|
2016
2609
|
});
|
|
2017
2610
|
}
|
|
2018
2611
|
//#endregion
|
|
@@ -2034,10 +2627,7 @@ function toEditor(model) {
|
|
|
2034
2627
|
const uidOf = (name) => tables.find((table) => table.name === name)?.uid ?? "";
|
|
2035
2628
|
const positions = Object.fromEntries(Object.entries(model.diagram ?? {}).flatMap(([name, point]) => {
|
|
2036
2629
|
const uid = uidOf(name);
|
|
2037
|
-
return uid ? [[uid, {
|
|
2038
|
-
x: point.x,
|
|
2039
|
-
y: point.y
|
|
2040
|
-
}]] : [];
|
|
2630
|
+
return uid ? [[uid, { ...point }]] : [];
|
|
2041
2631
|
}));
|
|
2042
2632
|
return {
|
|
2043
2633
|
name: model.name,
|
|
@@ -2132,14 +2722,18 @@ function fromEditor(editor) {
|
|
|
2132
2722
|
...Object.keys(diagram).length && { diagram }
|
|
2133
2723
|
};
|
|
2134
2724
|
}
|
|
2135
|
-
/** Posições pelo nome, na ordem dos cards; arredonda (o arquivo não precisa de frações). */
|
|
2725
|
+
/** Posições (e tamanhos) pelo nome, na ordem dos cards; arredonda (o arquivo não precisa de frações). */
|
|
2136
2726
|
function diagramOf(editor) {
|
|
2137
2727
|
return Object.fromEntries(editor.tables.flatMap(({ uid, name }) => {
|
|
2138
2728
|
const point = editor.positions[uid];
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2729
|
+
if (!point || !name) return [];
|
|
2730
|
+
const { x, y, width, height } = point;
|
|
2731
|
+
return [[name, {
|
|
2732
|
+
x: Math.round(x),
|
|
2733
|
+
y: Math.round(y),
|
|
2734
|
+
...width !== void 0 && { width: Math.round(width) },
|
|
2735
|
+
...height !== void 0 && { height: Math.round(height) }
|
|
2736
|
+
}]];
|
|
2143
2737
|
}));
|
|
2144
2738
|
}
|
|
2145
2739
|
/** O modelo sem as posições do diagrama: o que muda as consultas. */
|
|
@@ -2918,6 +3512,11 @@ const HEADER = 34;
|
|
|
2918
3512
|
const ROW = 22;
|
|
2919
3513
|
const MAX_BODY = 264;
|
|
2920
3514
|
const PADDING = 4;
|
|
3515
|
+
/** Limites do redimensionamento do card. */
|
|
3516
|
+
const MIN_WIDTH = 160;
|
|
3517
|
+
const MAX_WIDTH = 800;
|
|
3518
|
+
const MIN_HEIGHT$1 = 64;
|
|
3519
|
+
const MAX_HEIGHT = 1600;
|
|
2921
3520
|
const ROLE_STYLE = {
|
|
2922
3521
|
one: {
|
|
2923
3522
|
color: "#4dabf7",
|
|
@@ -3034,25 +3633,27 @@ function buildNodes(editor, columns, errors) {
|
|
|
3034
3633
|
};
|
|
3035
3634
|
});
|
|
3036
3635
|
}
|
|
3037
|
-
|
|
3636
|
+
/** Altura do conteúdo (sem tamanho salvo): até `MAX_BODY` de linhas, o resto rola. */
|
|
3637
|
+
const contentHeight = (node) => HEADER + Math.min(node.items.length * ROW, MAX_BODY) + 8;
|
|
3638
|
+
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
3038
3639
|
/** Quem só filtra (lado 1) em cima, o resto embaixo (como costuma ficar no Power BI). */
|
|
3039
|
-
function autoLayout(nodes,
|
|
3640
|
+
function autoLayout(nodes, sizes) {
|
|
3040
3641
|
const top = nodes.filter((node) => node.role === "one");
|
|
3041
3642
|
const bottom = nodes.filter((node) => node.role !== "one");
|
|
3042
3643
|
const positions = {};
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3644
|
+
const gap = 60;
|
|
3645
|
+
const row = (list, y) => {
|
|
3646
|
+
let x = 40;
|
|
3647
|
+
for (const node of list) {
|
|
3648
|
+
positions[node.id] = {
|
|
3649
|
+
x,
|
|
3650
|
+
y
|
|
3651
|
+
};
|
|
3652
|
+
x += (sizes.get(node.id)?.width ?? CARD_WIDTH) + gap;
|
|
3653
|
+
}
|
|
3654
|
+
};
|
|
3655
|
+
row(top, 40);
|
|
3656
|
+
row(bottom, 40 + Math.max(0, ...top.map((node) => sizes.get(node.id)?.height ?? HEADER)) + 140);
|
|
3056
3657
|
return positions;
|
|
3057
3658
|
}
|
|
3058
3659
|
function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingColumns, onLoadColumns, onTry, usage }) {
|
|
@@ -3062,6 +3663,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3062
3663
|
errors
|
|
3063
3664
|
]);
|
|
3064
3665
|
const [moving, setMoving] = useState(null);
|
|
3666
|
+
const [resizing, setResizing] = useState(null);
|
|
3065
3667
|
const [collapsed, setCollapsed] = useState({});
|
|
3066
3668
|
const [scrolls, setScrolls] = useState({});
|
|
3067
3669
|
const [view, setView] = useState({
|
|
@@ -3099,9 +3701,39 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3099
3701
|
requested.current = true;
|
|
3100
3702
|
onLoadColumns();
|
|
3101
3703
|
}, [columns, onLoadColumns]);
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3104
|
-
|
|
3704
|
+
const sizes = useMemo(() => new Map(nodes.map((node) => {
|
|
3705
|
+
const saved = resizing?.id === node.id ? resizing : editor.positions[node.id];
|
|
3706
|
+
const width = saved?.width ?? CARD_WIDTH;
|
|
3707
|
+
if (collapsed[node.id]) return [node.id, {
|
|
3708
|
+
width,
|
|
3709
|
+
height: HEADER
|
|
3710
|
+
}];
|
|
3711
|
+
return [node.id, {
|
|
3712
|
+
width,
|
|
3713
|
+
height: saved?.height ?? contentHeight(node)
|
|
3714
|
+
}];
|
|
3715
|
+
})), [
|
|
3716
|
+
nodes,
|
|
3717
|
+
collapsed,
|
|
3718
|
+
editor.positions,
|
|
3719
|
+
resizing
|
|
3720
|
+
]);
|
|
3721
|
+
const sizeOf = (node) => sizes.get(node.id) ?? {
|
|
3722
|
+
width: CARD_WIDTH,
|
|
3723
|
+
height: HEADER
|
|
3724
|
+
};
|
|
3725
|
+
const fallback = useMemo(() => autoLayout(nodes, sizes), [nodes, sizes]);
|
|
3726
|
+
const placed = (node) => {
|
|
3727
|
+
if (moving?.id === node.id) return moving.point;
|
|
3728
|
+
const saved = editor.positions[node.id];
|
|
3729
|
+
return saved ? {
|
|
3730
|
+
x: saved.x,
|
|
3731
|
+
y: saved.y
|
|
3732
|
+
} : fallback[node.id] ?? {
|
|
3733
|
+
x: 0,
|
|
3734
|
+
y: 0
|
|
3735
|
+
};
|
|
3736
|
+
};
|
|
3105
3737
|
const setPositions = (update) => setEditor((current) => ({
|
|
3106
3738
|
...current,
|
|
3107
3739
|
positions: update(current.positions)
|
|
@@ -3110,7 +3742,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3110
3742
|
/** Ponto da linha na coluna do card (lado esquerdo ou direito). */
|
|
3111
3743
|
const anchor = (node, column, side) => {
|
|
3112
3744
|
const { x, y } = placed(node);
|
|
3113
|
-
const height =
|
|
3745
|
+
const { width, height } = sizeOf(node);
|
|
3114
3746
|
const index = node.items.findIndex((item) => item.kind === "column" && item.name === column);
|
|
3115
3747
|
let rowY = y + HEADER / 2;
|
|
3116
3748
|
if (!collapsed[node.id] && index >= 0) {
|
|
@@ -3118,7 +3750,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3118
3750
|
rowY = Math.min(Math.max(raw, y + HEADER + 2), y + height - 2);
|
|
3119
3751
|
}
|
|
3120
3752
|
return {
|
|
3121
|
-
x: side === "left" ? x : x +
|
|
3753
|
+
x: side === "left" ? x : x + width,
|
|
3122
3754
|
y: rowY
|
|
3123
3755
|
};
|
|
3124
3756
|
};
|
|
@@ -3298,11 +3930,56 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3298
3930
|
setMoving(null);
|
|
3299
3931
|
if (moved) setPositions((current) => ({
|
|
3300
3932
|
...current,
|
|
3301
|
-
[node.id]:
|
|
3933
|
+
[node.id]: {
|
|
3934
|
+
...current[node.id],
|
|
3935
|
+
...point
|
|
3936
|
+
}
|
|
3302
3937
|
}));
|
|
3303
3938
|
else selectNode(node);
|
|
3304
3939
|
});
|
|
3305
3940
|
};
|
|
3941
|
+
/** Arrasta a borda direita (`width`), a de baixo (`height`) ou o canto (as duas). */
|
|
3942
|
+
const startResize = (event, node, edges) => {
|
|
3943
|
+
if (event.button !== 0) return;
|
|
3944
|
+
const start = sizeOf(node);
|
|
3945
|
+
const { zoom } = view;
|
|
3946
|
+
let size = start;
|
|
3947
|
+
track(event, (dx, dy) => {
|
|
3948
|
+
size = {
|
|
3949
|
+
width: edges.width ? clamp(start.width + dx / zoom, MIN_WIDTH, MAX_WIDTH) : start.width,
|
|
3950
|
+
height: edges.height ? clamp(start.height + dy / zoom, MIN_HEIGHT$1, MAX_HEIGHT) : start.height
|
|
3951
|
+
};
|
|
3952
|
+
setResizing({
|
|
3953
|
+
id: node.id,
|
|
3954
|
+
...size
|
|
3955
|
+
});
|
|
3956
|
+
}, (_, moved) => {
|
|
3957
|
+
setResizing(null);
|
|
3958
|
+
if (!moved) return;
|
|
3959
|
+
const point = placed(node);
|
|
3960
|
+
setPositions((current) => ({
|
|
3961
|
+
...current,
|
|
3962
|
+
[node.id]: {
|
|
3963
|
+
...point,
|
|
3964
|
+
...current[node.id],
|
|
3965
|
+
...edges.width && { width: size.width },
|
|
3966
|
+
...edges.height && { height: size.height }
|
|
3967
|
+
}
|
|
3968
|
+
}));
|
|
3969
|
+
});
|
|
3970
|
+
};
|
|
3971
|
+
/** Duplo clique na alça: volta ao tamanho automático (largura padrão, altura do conteúdo). */
|
|
3972
|
+
const resetSize = (node) => setPositions((current) => {
|
|
3973
|
+
const saved = current[node.id];
|
|
3974
|
+
if (!saved) return current;
|
|
3975
|
+
return {
|
|
3976
|
+
...current,
|
|
3977
|
+
[node.id]: {
|
|
3978
|
+
x: saved.x,
|
|
3979
|
+
y: saved.y
|
|
3980
|
+
}
|
|
3981
|
+
};
|
|
3982
|
+
});
|
|
3306
3983
|
const startLink = (event, node, column) => {
|
|
3307
3984
|
if (event.button !== 0) return;
|
|
3308
3985
|
track(event, (_, __, moveEvent) => setLinking({
|
|
@@ -3355,11 +4032,12 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3355
4032
|
if (!rect || !nodes.length) return;
|
|
3356
4033
|
const boxes = nodes.map((node) => ({
|
|
3357
4034
|
...layout?.[node.id] ?? placed(node),
|
|
3358
|
-
|
|
4035
|
+
w: sizeOf(node).width,
|
|
4036
|
+
h: sizeOf(node).height
|
|
3359
4037
|
}));
|
|
3360
4038
|
const minX = Math.min(...boxes.map((box) => box.x));
|
|
3361
4039
|
const minY = Math.min(...boxes.map((box) => box.y));
|
|
3362
|
-
const maxX = Math.max(...boxes.map((box) => box.x +
|
|
4040
|
+
const maxX = Math.max(...boxes.map((box) => box.x + box.w));
|
|
3363
4041
|
const maxY = Math.max(...boxes.map((box) => box.y + box.h));
|
|
3364
4042
|
const zoom = Math.min(1.2, Math.max(.3, Math.min((rect.width - 60) / (maxX - minX), (rect.height - 60) / (maxY - minY))));
|
|
3365
4043
|
setView({
|
|
@@ -3375,7 +4053,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3375
4053
|
const isSelected = (node) => selection?.kind === "table" && selection.uid === node.uid;
|
|
3376
4054
|
const lines = [];
|
|
3377
4055
|
const pushLine = (id, many, manyColumn, one, oneColumn, extra) => {
|
|
3378
|
-
const rightward = placed(many).x +
|
|
4056
|
+
const rightward = placed(many).x + sizeOf(many).width / 2 <= placed(one).x + sizeOf(one).width / 2;
|
|
3379
4057
|
lines.push({
|
|
3380
4058
|
id,
|
|
3381
4059
|
from: anchor(many, manyColumn, rightward ? "right" : "left"),
|
|
@@ -3462,7 +4140,7 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3462
4140
|
}), nodes.map((node) => /* @__PURE__ */ jsx(TableCard, {
|
|
3463
4141
|
node,
|
|
3464
4142
|
position: placed(node),
|
|
3465
|
-
|
|
4143
|
+
size: sizeOf(node),
|
|
3466
4144
|
collapsed: !!collapsed[node.id],
|
|
3467
4145
|
selected: !!isSelected(node),
|
|
3468
4146
|
linking: !!linking && linking.node.id !== node.id,
|
|
@@ -3476,6 +4154,8 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3476
4154
|
[node.id]: top
|
|
3477
4155
|
})),
|
|
3478
4156
|
onHeaderDown: (event) => startMove(event, node),
|
|
4157
|
+
onResizeDown: (event, edges) => startResize(event, node, edges),
|
|
4158
|
+
onResizeReset: () => resetSize(node),
|
|
3479
4159
|
onColumnDown: (event, column) => startLink(event, node, column),
|
|
3480
4160
|
onFieldClick: (item) => setSelection({
|
|
3481
4161
|
kind: item.kind,
|
|
@@ -3532,8 +4212,11 @@ function ModelDiagram({ editor, setEditor, columns, errors, warnings, loadingCol
|
|
|
3532
4212
|
/* @__PURE__ */ jsx(Button, {
|
|
3533
4213
|
size: "small",
|
|
3534
4214
|
onClick: () => {
|
|
3535
|
-
const layout = autoLayout(nodes,
|
|
3536
|
-
setPositions(() => layout)
|
|
4215
|
+
const layout = autoLayout(nodes, sizes);
|
|
4216
|
+
setPositions((current) => Object.fromEntries(Object.entries(layout).map(([id, point]) => [id, {
|
|
4217
|
+
...current[id],
|
|
4218
|
+
...point
|
|
4219
|
+
}])));
|
|
3537
4220
|
fitToScreen(layout);
|
|
3538
4221
|
},
|
|
3539
4222
|
title: "Dimensões em cima, fatos embaixo (troca as posições salvas)",
|
|
@@ -3842,7 +4525,7 @@ function RelationshipLine({ line, hovered, onHover }) {
|
|
|
3842
4525
|
]
|
|
3843
4526
|
});
|
|
3844
4527
|
}
|
|
3845
|
-
function TableCard({ node, position,
|
|
4528
|
+
function TableCard({ node, position, size, collapsed, selected, linking, selectedField, onToggle, onScroll, onHeaderDown, onResizeDown, onResizeReset, onColumnDown, onFieldClick, onAddField }) {
|
|
3846
4529
|
const style = ROLE_STYLE[node.role];
|
|
3847
4530
|
const columnActions = ["dimension", "measure"];
|
|
3848
4531
|
return /* @__PURE__ */ jsxs(Box, {
|
|
@@ -3851,8 +4534,8 @@ function TableCard({ node, position, height, collapsed, selected, linking, selec
|
|
|
3851
4534
|
position: "absolute",
|
|
3852
4535
|
left: position.x,
|
|
3853
4536
|
top: position.y,
|
|
3854
|
-
width:
|
|
3855
|
-
height,
|
|
4537
|
+
width: size.width,
|
|
4538
|
+
height: size.height,
|
|
3856
4539
|
bgcolor: "#23272e",
|
|
3857
4540
|
border: "1px solid",
|
|
3858
4541
|
borderColor: node.error ? "error.main" : selected ? style.color : "#3a3f47",
|
|
@@ -3864,187 +4547,252 @@ function TableCard({ node, position, height, collapsed, selected, linking, selec
|
|
|
3864
4547
|
userSelect: "none",
|
|
3865
4548
|
cursor: "default"
|
|
3866
4549
|
},
|
|
3867
|
-
children: [
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
height: 31,
|
|
3872
|
-
display: "flex",
|
|
3873
|
-
alignItems: "center",
|
|
3874
|
-
gap: .75,
|
|
3875
|
-
px: 1,
|
|
3876
|
-
cursor: "move",
|
|
3877
|
-
borderBottom: collapsed ? 0 : "1px solid #3a3f47",
|
|
3878
|
-
flexShrink: 0
|
|
3879
|
-
},
|
|
3880
|
-
children: [
|
|
3881
|
-
/* @__PURE__ */ jsx(Box, {
|
|
3882
|
-
component: "span",
|
|
3883
|
-
sx: {
|
|
3884
|
-
color: style.color,
|
|
3885
|
-
fontSize: 13
|
|
3886
|
-
},
|
|
3887
|
-
children: "▦"
|
|
3888
|
-
}),
|
|
3889
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
3890
|
-
sx: {
|
|
3891
|
-
fontWeight: 700,
|
|
3892
|
-
fontSize: 13,
|
|
3893
|
-
overflow: "hidden",
|
|
3894
|
-
textOverflow: "ellipsis",
|
|
3895
|
-
whiteSpace: "nowrap",
|
|
3896
|
-
flex: 1
|
|
3897
|
-
},
|
|
3898
|
-
children: node.name || /* @__PURE__ */ jsx("em", { children: "sem nome" })
|
|
3899
|
-
}),
|
|
3900
|
-
/* @__PURE__ */ jsx(Box, {
|
|
3901
|
-
component: "span",
|
|
3902
|
-
title: style.title,
|
|
3903
|
-
sx: {
|
|
3904
|
-
fontSize: 10,
|
|
3905
|
-
color: style.color
|
|
3906
|
-
},
|
|
3907
|
-
children: style.label
|
|
3908
|
-
}),
|
|
3909
|
-
node.error && /* @__PURE__ */ jsx(Box, {
|
|
3910
|
-
component: "span",
|
|
3911
|
-
sx: {
|
|
3912
|
-
color: "error.main",
|
|
3913
|
-
fontSize: 12
|
|
3914
|
-
},
|
|
3915
|
-
children: "⚠"
|
|
3916
|
-
}),
|
|
3917
|
-
/* @__PURE__ */ jsx(Box, {
|
|
3918
|
-
component: "button",
|
|
3919
|
-
type: "button",
|
|
3920
|
-
"aria-label": collapsed ? "Expandir" : "Recolher",
|
|
3921
|
-
onMouseDown: (event) => event.stopPropagation(),
|
|
3922
|
-
onClick: onToggle,
|
|
3923
|
-
sx: {
|
|
3924
|
-
border: 0,
|
|
3925
|
-
bgcolor: "transparent",
|
|
3926
|
-
color: "text.secondary",
|
|
3927
|
-
cursor: "pointer",
|
|
3928
|
-
p: 0,
|
|
3929
|
-
fontSize: 11
|
|
3930
|
-
},
|
|
3931
|
-
children: collapsed ? "▾" : "▴"
|
|
3932
|
-
})
|
|
3933
|
-
]
|
|
3934
|
-
}), !collapsed && /* @__PURE__ */ jsxs(Box, {
|
|
3935
|
-
onScroll: (event) => onScroll(event.currentTarget.scrollTop),
|
|
3936
|
-
sx: {
|
|
3937
|
-
overflow: "auto",
|
|
3938
|
-
flex: 1,
|
|
3939
|
-
py: `${PADDING}px`
|
|
3940
|
-
},
|
|
3941
|
-
children: [!node.items.length && /* @__PURE__ */ jsx(Typography, {
|
|
3942
|
-
sx: {
|
|
3943
|
-
fontSize: 11,
|
|
3944
|
-
color: "text.disabled",
|
|
3945
|
-
px: 1,
|
|
3946
|
-
lineHeight: `${ROW}px`
|
|
3947
|
-
},
|
|
3948
|
-
children: "sem colunas lidas"
|
|
3949
|
-
}), node.items.map((item) => item.kind === "column" ? /* @__PURE__ */ jsxs(Box, {
|
|
3950
|
-
"data-diagram-node": node.id,
|
|
3951
|
-
"data-diagram-column": item.name,
|
|
3952
|
-
onMouseDown: (event) => onColumnDown(event, item.name),
|
|
3953
|
-
title: item.missing ? "Citada no modelo, mas a tabela não devolve essa coluna" : "Arraste até a coluna de outra tabela para relacionar",
|
|
4550
|
+
children: [
|
|
4551
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
4552
|
+
onMouseDown: onHeaderDown,
|
|
4553
|
+
title: node.error,
|
|
3954
4554
|
sx: {
|
|
3955
|
-
height:
|
|
4555
|
+
height: 31,
|
|
3956
4556
|
display: "flex",
|
|
3957
4557
|
alignItems: "center",
|
|
3958
4558
|
gap: .75,
|
|
3959
4559
|
px: 1,
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
"&:hover .column-actions": { display: linking ? "none" : "flex" },
|
|
3964
|
-
color: item.missing ? "error.main" : item.linked ? "text.primary" : "#adb5bd",
|
|
3965
|
-
fontWeight: item.linked ? 700 : 400,
|
|
3966
|
-
"&:hover": { bgcolor: linking ? "rgba(245,130,32,.25)" : "rgba(255,255,255,.06)" }
|
|
4560
|
+
cursor: "move",
|
|
4561
|
+
borderBottom: collapsed ? 0 : "1px solid #3a3f47",
|
|
4562
|
+
flexShrink: 0
|
|
3967
4563
|
},
|
|
3968
4564
|
children: [
|
|
3969
4565
|
/* @__PURE__ */ jsx(Box, {
|
|
3970
4566
|
component: "span",
|
|
3971
4567
|
sx: {
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
color: "text.secondary"
|
|
4568
|
+
color: style.color,
|
|
4569
|
+
fontSize: 13
|
|
3975
4570
|
},
|
|
3976
|
-
children:
|
|
4571
|
+
children: "▦"
|
|
3977
4572
|
}),
|
|
3978
|
-
/* @__PURE__ */ jsx(
|
|
3979
|
-
component: "span",
|
|
4573
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
3980
4574
|
sx: {
|
|
4575
|
+
fontWeight: 700,
|
|
4576
|
+
fontSize: 13,
|
|
3981
4577
|
overflow: "hidden",
|
|
3982
4578
|
textOverflow: "ellipsis",
|
|
3983
|
-
whiteSpace: "nowrap"
|
|
4579
|
+
whiteSpace: "nowrap",
|
|
4580
|
+
flex: 1
|
|
3984
4581
|
},
|
|
3985
|
-
children:
|
|
4582
|
+
children: node.name || /* @__PURE__ */ jsx("em", { children: "sem nome" })
|
|
3986
4583
|
}),
|
|
3987
4584
|
/* @__PURE__ */ jsx(Box, {
|
|
3988
|
-
|
|
4585
|
+
component: "span",
|
|
4586
|
+
title: style.title,
|
|
3989
4587
|
sx: {
|
|
3990
|
-
|
|
3991
|
-
|
|
4588
|
+
fontSize: 10,
|
|
4589
|
+
color: style.color
|
|
3992
4590
|
},
|
|
3993
|
-
children:
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4591
|
+
children: style.label
|
|
4592
|
+
}),
|
|
4593
|
+
node.error && /* @__PURE__ */ jsx(Box, {
|
|
4594
|
+
component: "span",
|
|
4595
|
+
sx: {
|
|
4596
|
+
color: "error.main",
|
|
4597
|
+
fontSize: 12
|
|
4598
|
+
},
|
|
4599
|
+
children: "⚠"
|
|
4600
|
+
}),
|
|
4601
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4602
|
+
component: "button",
|
|
4603
|
+
type: "button",
|
|
4604
|
+
"aria-label": collapsed ? "Expandir" : "Recolher",
|
|
4605
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
4606
|
+
onClick: onToggle,
|
|
4607
|
+
sx: {
|
|
4608
|
+
border: 0,
|
|
4609
|
+
bgcolor: "transparent",
|
|
4610
|
+
color: "text.secondary",
|
|
4611
|
+
cursor: "pointer",
|
|
4612
|
+
p: 0,
|
|
4613
|
+
fontSize: 11
|
|
4614
|
+
},
|
|
4615
|
+
children: collapsed ? "▾" : "▴"
|
|
4011
4616
|
})
|
|
4012
4617
|
]
|
|
4013
|
-
}
|
|
4014
|
-
|
|
4015
|
-
|
|
4618
|
+
}),
|
|
4619
|
+
!collapsed && /* @__PURE__ */ jsxs(Box, {
|
|
4620
|
+
onScroll: (event) => onScroll(event.currentTarget.scrollTop),
|
|
4016
4621
|
sx: {
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
gap: .75,
|
|
4021
|
-
px: 1,
|
|
4022
|
-
fontSize: 12,
|
|
4023
|
-
cursor: "pointer",
|
|
4024
|
-
color: item.kind === "measure" ? "#ffd8a8" : "#a5d8ff",
|
|
4025
|
-
bgcolor: selectedField === item.uid ? "rgba(245,130,32,.2)" : void 0,
|
|
4026
|
-
"&:hover": { bgcolor: "rgba(255,255,255,.06)" }
|
|
4622
|
+
overflow: "auto",
|
|
4623
|
+
flex: 1,
|
|
4624
|
+
py: `${PADDING}px`
|
|
4027
4625
|
},
|
|
4028
|
-
children: [/* @__PURE__ */ jsx(
|
|
4029
|
-
component: "span",
|
|
4626
|
+
children: [!node.items.length && /* @__PURE__ */ jsx(Typography, {
|
|
4030
4627
|
sx: {
|
|
4031
|
-
|
|
4032
|
-
|
|
4628
|
+
fontSize: 11,
|
|
4629
|
+
color: "text.disabled",
|
|
4630
|
+
px: 1,
|
|
4631
|
+
lineHeight: `${ROW}px`
|
|
4033
4632
|
},
|
|
4034
|
-
children:
|
|
4035
|
-
}), /* @__PURE__ */
|
|
4036
|
-
|
|
4633
|
+
children: "sem colunas lidas"
|
|
4634
|
+
}), node.items.map((item) => item.kind === "column" ? /* @__PURE__ */ jsxs(Box, {
|
|
4635
|
+
"data-diagram-node": node.id,
|
|
4636
|
+
"data-diagram-column": item.name,
|
|
4637
|
+
onMouseDown: (event) => onColumnDown(event, item.name),
|
|
4638
|
+
title: item.missing ? "Citada no modelo, mas a tabela não devolve essa coluna" : "Arraste até a coluna de outra tabela para relacionar",
|
|
4037
4639
|
sx: {
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4640
|
+
height: ROW,
|
|
4641
|
+
display: "flex",
|
|
4642
|
+
alignItems: "center",
|
|
4643
|
+
gap: .75,
|
|
4644
|
+
px: 1,
|
|
4645
|
+
fontSize: 12,
|
|
4646
|
+
cursor: "crosshair",
|
|
4647
|
+
"& .column-actions": { display: "none" },
|
|
4648
|
+
"&:hover .column-actions": { display: linking ? "none" : "flex" },
|
|
4649
|
+
color: item.missing ? "error.main" : item.linked ? "text.primary" : "#adb5bd",
|
|
4650
|
+
fontWeight: item.linked ? 700 : 400,
|
|
4651
|
+
"&:hover": { bgcolor: linking ? "rgba(245,130,32,.25)" : "rgba(255,255,255,.06)" }
|
|
4041
4652
|
},
|
|
4042
|
-
children:
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4653
|
+
children: [
|
|
4654
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4655
|
+
component: "span",
|
|
4656
|
+
sx: {
|
|
4657
|
+
width: 14,
|
|
4658
|
+
textAlign: "center",
|
|
4659
|
+
color: "text.secondary"
|
|
4660
|
+
},
|
|
4661
|
+
children: item.isKey ? "🔑" : item.numeric ? "Σ" : ""
|
|
4662
|
+
}),
|
|
4663
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4664
|
+
component: "span",
|
|
4665
|
+
sx: {
|
|
4666
|
+
overflow: "hidden",
|
|
4667
|
+
textOverflow: "ellipsis",
|
|
4668
|
+
whiteSpace: "nowrap"
|
|
4669
|
+
},
|
|
4670
|
+
children: item.name
|
|
4671
|
+
}),
|
|
4672
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4673
|
+
className: "column-actions",
|
|
4674
|
+
sx: {
|
|
4675
|
+
ml: "auto",
|
|
4676
|
+
gap: .25
|
|
4677
|
+
},
|
|
4678
|
+
children: columnActions.map((kind) => /* @__PURE__ */ jsx(Box, {
|
|
4679
|
+
component: "button",
|
|
4680
|
+
type: "button",
|
|
4681
|
+
title: kind === "dimension" ? `Nova dimensão com ${node.name}.${item.name}` : `Nova medida: ${item.numeric ? "soma" : "contagem"} de ${node.name}.${item.name}`,
|
|
4682
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
4683
|
+
onClick: () => onAddField(kind, item),
|
|
4684
|
+
sx: {
|
|
4685
|
+
border: "1px solid #3a3f47",
|
|
4686
|
+
borderRadius: .5,
|
|
4687
|
+
bgcolor: "#1b1e23",
|
|
4688
|
+
color: kind === "measure" ? "#ffd8a8" : "#a5d8ff",
|
|
4689
|
+
cursor: "pointer",
|
|
4690
|
+
fontSize: 10,
|
|
4691
|
+
lineHeight: "14px",
|
|
4692
|
+
px: .5
|
|
4693
|
+
},
|
|
4694
|
+
children: kind === "dimension" ? "+◇" : "+Σ"
|
|
4695
|
+
}, kind))
|
|
4696
|
+
})
|
|
4697
|
+
]
|
|
4698
|
+
}, `c:${item.name}`) : /* @__PURE__ */ jsxs(Box, {
|
|
4699
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
4700
|
+
onClick: () => onFieldClick(item),
|
|
4701
|
+
sx: {
|
|
4702
|
+
height: ROW,
|
|
4703
|
+
display: "flex",
|
|
4704
|
+
alignItems: "center",
|
|
4705
|
+
gap: .75,
|
|
4706
|
+
px: 1,
|
|
4707
|
+
fontSize: 12,
|
|
4708
|
+
cursor: "pointer",
|
|
4709
|
+
color: item.kind === "measure" ? "#ffd8a8" : "#a5d8ff",
|
|
4710
|
+
bgcolor: selectedField === item.uid ? "rgba(245,130,32,.2)" : void 0,
|
|
4711
|
+
"&:hover": { bgcolor: "rgba(255,255,255,.06)" }
|
|
4712
|
+
},
|
|
4713
|
+
children: [/* @__PURE__ */ jsx(Box, {
|
|
4714
|
+
component: "span",
|
|
4715
|
+
sx: {
|
|
4716
|
+
width: 14,
|
|
4717
|
+
textAlign: "center"
|
|
4718
|
+
},
|
|
4719
|
+
children: item.icon
|
|
4720
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
4721
|
+
component: "span",
|
|
4722
|
+
sx: {
|
|
4723
|
+
overflow: "hidden",
|
|
4724
|
+
textOverflow: "ellipsis",
|
|
4725
|
+
whiteSpace: "nowrap"
|
|
4726
|
+
},
|
|
4727
|
+
children: item.name || /* @__PURE__ */ jsx("em", { children: "sem nome" })
|
|
4728
|
+
})]
|
|
4729
|
+
}, `f:${item.uid}`))]
|
|
4730
|
+
}),
|
|
4731
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4732
|
+
onMouseDown: (event) => onResizeDown(event, {
|
|
4733
|
+
width: true,
|
|
4734
|
+
height: false
|
|
4735
|
+
}),
|
|
4736
|
+
onDoubleClick: onResizeReset,
|
|
4737
|
+
title: "Arraste para mudar a largura (duplo clique: tamanho automático)",
|
|
4738
|
+
sx: {
|
|
4739
|
+
...resizeHandleSx,
|
|
4740
|
+
top: 0,
|
|
4741
|
+
right: -4,
|
|
4742
|
+
bottom: 8,
|
|
4743
|
+
width: 8,
|
|
4744
|
+
cursor: "ew-resize"
|
|
4745
|
+
}
|
|
4746
|
+
}),
|
|
4747
|
+
!collapsed && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Box, {
|
|
4748
|
+
onMouseDown: (event) => onResizeDown(event, {
|
|
4749
|
+
width: false,
|
|
4750
|
+
height: true
|
|
4751
|
+
}),
|
|
4752
|
+
onDoubleClick: onResizeReset,
|
|
4753
|
+
title: "Arraste para mudar a altura (duplo clique: tamanho automático)",
|
|
4754
|
+
sx: {
|
|
4755
|
+
...resizeHandleSx,
|
|
4756
|
+
left: 0,
|
|
4757
|
+
right: 8,
|
|
4758
|
+
bottom: -4,
|
|
4759
|
+
height: 8,
|
|
4760
|
+
cursor: "ns-resize"
|
|
4761
|
+
}
|
|
4762
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
4763
|
+
onMouseDown: (event) => onResizeDown(event, {
|
|
4764
|
+
width: true,
|
|
4765
|
+
height: true
|
|
4766
|
+
}),
|
|
4767
|
+
onDoubleClick: onResizeReset,
|
|
4768
|
+
title: "Arraste para redimensionar (duplo clique: tamanho automático)",
|
|
4769
|
+
sx: {
|
|
4770
|
+
...resizeHandleSx,
|
|
4771
|
+
right: -4,
|
|
4772
|
+
bottom: -4,
|
|
4773
|
+
width: 14,
|
|
4774
|
+
height: 14,
|
|
4775
|
+
cursor: "nwse-resize",
|
|
4776
|
+
"&::after": {
|
|
4777
|
+
content: "\"\"",
|
|
4778
|
+
position: "absolute",
|
|
4779
|
+
right: 6,
|
|
4780
|
+
bottom: 6,
|
|
4781
|
+
width: 6,
|
|
4782
|
+
height: 6,
|
|
4783
|
+
borderRight: "2px solid #5c636e",
|
|
4784
|
+
borderBottom: "2px solid #5c636e"
|
|
4785
|
+
},
|
|
4786
|
+
"&:hover::after": { borderColor: ROLE_STYLE[node.role].color }
|
|
4787
|
+
}
|
|
4788
|
+
})] })
|
|
4789
|
+
]
|
|
4046
4790
|
});
|
|
4047
4791
|
}
|
|
4792
|
+
const resizeHandleSx = {
|
|
4793
|
+
position: "absolute",
|
|
4794
|
+
zIndex: 1
|
|
4795
|
+
};
|
|
4048
4796
|
function PropertyBlock({ title, children }) {
|
|
4049
4797
|
return /* @__PURE__ */ jsxs(Box, {
|
|
4050
4798
|
sx: {
|
|
@@ -6883,4 +7631,4 @@ function ToggleButton$1({ draft, loading, problems, inspecting, onOpen }) {
|
|
|
6883
7631
|
//#endregion
|
|
6884
7632
|
export { DashboardDevtools as default };
|
|
6885
7633
|
|
|
6886
|
-
//# sourceMappingURL=DashboardDevtools-
|
|
7634
|
+
//# sourceMappingURL=DashboardDevtools-6v5HDe-A.js.map
|