@bluemarble/bi 0.5.0 → 0.6.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-6v5HDe-A.js → DashboardDevtools-SCewfQGR.js} +508 -28
- package/dist/DashboardDevtools-SCewfQGR.js.map +1 -0
- package/dist/{ModelDictionary-D3JzCd6x.js → ModelDictionary-C2tzlOY8.js} +4 -3
- package/dist/{ModelDictionary-D3JzCd6x.js.map → ModelDictionary-C2tzlOY8.js.map} +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +164 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.d.ts +26 -5
- package/dist/client/index.js +72 -26
- package/dist/client/index.js.map +1 -1
- package/dist/codegen-DBe4-XW_.js +354 -0
- package/dist/codegen-DBe4-XW_.js.map +1 -0
- package/dist/{types-FdrY0FOZ.d.ts → layout-B7eYgNyR.d.ts} +226 -152
- package/dist/{layout-QuOYFmp1.js → layout-BS51SQcJ.js} +219 -346
- package/dist/layout-BS51SQcJ.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +4 -1
- package/dist/DashboardDevtools-6v5HDe-A.js.map +0 -1
- package/dist/layout-QuOYFmp1.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as referencedTables, a as AGGREGATIONS, d as validateModel, h as parseColumnRef, i as toModelCode, l as isAggregate, m as modelEdges, o as AGGREGATION_LABEL, p as measureHome, s as VALUE_FORMATS, t as canonicalJson } from "./codegen-DBe4-XW_.js";
|
|
2
2
|
import { n as diffRows } from "./compare-DloZhg1T.js";
|
|
3
|
-
import {
|
|
3
|
+
import { C as RULE_OPERATOR_LABEL, S as RULE_OPERATORS, _ as tableColumns, a as FILTER_FIELDS, b as COLUMN_VERTICAL_ALIGNS, c as chartOfType, d as filterReferences, h as percentColumn, i as CARD_FIELDS, 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 validateLayout, w as UNARY_OPERATORS, x as NUMERIC_OPERATORS, y as COLUMN_ALIGNS } from "./layout-BS51SQcJ.js";
|
|
4
|
+
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-C2tzlOY8.js";
|
|
4
5
|
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
6
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1761,23 +1762,84 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1761
1762
|
label: measure.label
|
|
1762
1763
|
}));
|
|
1763
1764
|
const entries = Object.entries(fields);
|
|
1765
|
+
const columnLabel = (name) => name.startsWith("%:") ? `% ${fieldLabelOf(model, name.slice(2))}` : fieldLabelOf(model, name);
|
|
1766
|
+
const columnOptionsOf = (names) => names.map((name) => ({
|
|
1767
|
+
name,
|
|
1768
|
+
label: columnLabel(name)
|
|
1769
|
+
}));
|
|
1770
|
+
const fieldOptions = [
|
|
1771
|
+
...columnOptionsOf(columns).map((option) => ({
|
|
1772
|
+
...option,
|
|
1773
|
+
group: "Colunas do gráfico"
|
|
1774
|
+
})),
|
|
1775
|
+
...dimensions.filter((option) => !columns.includes(option.name)).map((option) => ({
|
|
1776
|
+
...option,
|
|
1777
|
+
group: "Dimensões fora do gráfico"
|
|
1778
|
+
})),
|
|
1779
|
+
...measures.filter((option) => !columns.includes(option.name)).map((option) => ({
|
|
1780
|
+
...option,
|
|
1781
|
+
group: "Medidas fora do gráfico"
|
|
1782
|
+
}))
|
|
1783
|
+
];
|
|
1764
1784
|
const keyOf = (kind) => entries.find(([, spec]) => spec.kind === kind)?.[0];
|
|
1765
1785
|
const labelsKey = keyOf("labels");
|
|
1766
1786
|
const formatsKey = keyOf("formats");
|
|
1767
|
-
const
|
|
1787
|
+
const alignsKey = keyOf("aligns");
|
|
1788
|
+
const verticalKey = keyOf("verticalAligns");
|
|
1789
|
+
const columnOptions = !!(labelsKey || formatsKey || alignsKey || verticalKey);
|
|
1768
1790
|
const formattersKey = columnOptions ? keyOf("formatters") : void 0;
|
|
1769
1791
|
const single = (kind) => entries.filter(([, spec]) => spec.kind === kind);
|
|
1770
1792
|
const formatterKey = columnOptions && single("dimension").length === 1 && single("formatter").length === 1 ? single("formatter")[0]?.[0] : void 0;
|
|
1771
1793
|
const labels = labelsKey && values[labelsKey] || {};
|
|
1772
1794
|
const formats = formatsKey && values[formatsKey] || {};
|
|
1795
|
+
const aligns = alignsKey && values[alignsKey] || {};
|
|
1796
|
+
const verticals = verticalKey && values[verticalKey] || {};
|
|
1797
|
+
const alignProps = (column, defaultAlign) => ({
|
|
1798
|
+
align: alignsKey ? aligns[column] ?? "" : void 0,
|
|
1799
|
+
defaultAlign,
|
|
1800
|
+
onAlign: (next) => alignsKey && set(alignsKey, pruned(withField(aligns, column, next), columns)),
|
|
1801
|
+
verticalAlign: verticalKey ? verticals[column] ?? "" : void 0,
|
|
1802
|
+
onVerticalAlign: (next) => verticalKey && set(verticalKey, pruned(withField(verticals, column, next), columns))
|
|
1803
|
+
});
|
|
1804
|
+
const resetAligns = (column, changes) => {
|
|
1805
|
+
if (alignsKey) changes[alignsKey] = pruned(withField(aligns, column, void 0), columns);
|
|
1806
|
+
if (verticalKey) changes[verticalKey] = pruned(withField(verticals, column, void 0), columns);
|
|
1807
|
+
};
|
|
1808
|
+
const setChanges = (changes) => {
|
|
1809
|
+
if (onSetMany) onSetMany(changes);
|
|
1810
|
+
else for (const [key, value] of Object.entries(changes)) set(key, value);
|
|
1811
|
+
};
|
|
1773
1812
|
const dimensionFormatters = formattersKey && values[formattersKey] || {};
|
|
1774
1813
|
const optionsButton = (kind, name) => {
|
|
1775
1814
|
if (!columnOptions || !name) return null;
|
|
1815
|
+
if (kind === "percent") {
|
|
1816
|
+
if (!alignsKey && !verticalKey) return null;
|
|
1817
|
+
const column = percentColumn(name);
|
|
1818
|
+
return /* @__PURE__ */ jsx(ColumnOptions, {
|
|
1819
|
+
kind,
|
|
1820
|
+
name,
|
|
1821
|
+
model,
|
|
1822
|
+
label: void 0,
|
|
1823
|
+
onLabel: () => {},
|
|
1824
|
+
format: void 0,
|
|
1825
|
+
onFormat: () => {},
|
|
1826
|
+
formatter: void 0,
|
|
1827
|
+
formatters,
|
|
1828
|
+
onFormatter: () => {},
|
|
1829
|
+
...alignProps(column, "right"),
|
|
1830
|
+
onReset: () => {
|
|
1831
|
+
const changes = {};
|
|
1832
|
+
resetAligns(column, changes);
|
|
1833
|
+
setChanges(changes);
|
|
1834
|
+
}
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1776
1837
|
const canFormatter = kind === "dimension" && !!(formattersKey || formatterKey);
|
|
1777
1838
|
return /* @__PURE__ */ jsx(ColumnOptions, {
|
|
1778
1839
|
kind,
|
|
1779
1840
|
name,
|
|
1780
1841
|
model,
|
|
1842
|
+
...alignProps(name, kind === "dimension" ? "left" : "right"),
|
|
1781
1843
|
label: labelsKey ? labels[name] ?? "" : void 0,
|
|
1782
1844
|
onLabel: (text) => labelsKey && set(labelsKey, pruned(withField(labels, name, text), columns)),
|
|
1783
1845
|
format: kind === "measure" && formatsKey ? formats[name] ?? {} : void 0,
|
|
@@ -1797,6 +1859,7 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1797
1859
|
onReset: () => {
|
|
1798
1860
|
const changes = {};
|
|
1799
1861
|
if (labelsKey) changes[labelsKey] = pruned(withField(labels, name, void 0), columns);
|
|
1862
|
+
resetAligns(name, changes);
|
|
1800
1863
|
if (formatsKey && kind === "measure") changes[formatsKey] = pruned(withField(formats, name, void 0), chosenMeasures);
|
|
1801
1864
|
if (canFormatter && formattersKey) changes[formattersKey] = pruned(withField(dimensionFormatters, name, void 0), chosenDimensions);
|
|
1802
1865
|
else if (canFormatter && formatterKey) changes[formatterKey] = void 0;
|
|
@@ -1808,14 +1871,17 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1808
1871
|
const merged = (() => {
|
|
1809
1872
|
for (const [dimensionsKey, spec] of entries) {
|
|
1810
1873
|
if (spec.kind !== "dimensions") continue;
|
|
1811
|
-
const pair = entries.find(([, other]) => other.kind === "measures" && other.section === spec.section);
|
|
1874
|
+
const pair = entries.find(([, other]) => other.kind === "measures" && !other.percent && other.section === spec.section);
|
|
1812
1875
|
if (!pair) continue;
|
|
1813
1876
|
const order = entries.find(([, other]) => other.kind === "columnOrder")?.[0];
|
|
1877
|
+
const percents = order ? entries.find(([, other]) => other.kind === "measures" && other.percent && other.section === spec.section) : void 0;
|
|
1814
1878
|
return {
|
|
1815
1879
|
dimensions: dimensionsKey,
|
|
1816
1880
|
dimensionsSpec: spec,
|
|
1817
1881
|
measures: pair[0],
|
|
1818
1882
|
measuresSpec: pair[1],
|
|
1883
|
+
percents: percents?.[0],
|
|
1884
|
+
percentsSpec: percents?.[1],
|
|
1819
1885
|
order
|
|
1820
1886
|
};
|
|
1821
1887
|
}
|
|
@@ -1823,18 +1889,26 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1823
1889
|
const mergedInput = (pair) => {
|
|
1824
1890
|
const dimensionNames = values[pair.dimensions] ?? [];
|
|
1825
1891
|
const measureNames = values[pair.measures] ?? [];
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
name
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1892
|
+
const percentNames = pair.percents ? values[pair.percents] ?? [] : [];
|
|
1893
|
+
const all = [
|
|
1894
|
+
...dimensionNames.map((name) => ({
|
|
1895
|
+
kind: "dimension",
|
|
1896
|
+
name
|
|
1897
|
+
})),
|
|
1898
|
+
...measureNames.map((name) => ({
|
|
1899
|
+
kind: "measure",
|
|
1900
|
+
name
|
|
1901
|
+
})),
|
|
1902
|
+
...percentNames.map((name) => ({
|
|
1903
|
+
kind: "percent",
|
|
1904
|
+
name
|
|
1905
|
+
}))
|
|
1906
|
+
];
|
|
1833
1907
|
const order = pair.order ? values[pair.order] : void 0;
|
|
1834
|
-
const
|
|
1908
|
+
const sortedColumns = tableColumns([...dimensionNames, ...measureNames], percentNames, order);
|
|
1835
1909
|
const pool = [...all];
|
|
1836
|
-
const items =
|
|
1837
|
-
const index = pool.findIndex((item) => item
|
|
1910
|
+
const items = sortedColumns.map((column) => {
|
|
1911
|
+
const index = pool.findIndex((item) => itemColumn(item) === column);
|
|
1838
1912
|
return pool.splice(index, 1)[0];
|
|
1839
1913
|
});
|
|
1840
1914
|
const required = !!pair.dimensionsSpec.required || !!pair.measuresSpec.required;
|
|
@@ -1842,19 +1916,22 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1842
1916
|
items,
|
|
1843
1917
|
options: {
|
|
1844
1918
|
dimension: options(pair.dimensionsSpec),
|
|
1845
|
-
measure: options(pair.measuresSpec)
|
|
1919
|
+
measure: options(pair.measuresSpec),
|
|
1920
|
+
...pair.percentsSpec && { percent: options(pair.percentsSpec) }
|
|
1846
1921
|
},
|
|
1847
1922
|
extra: (item) => optionsButton(item.kind, item.name),
|
|
1848
1923
|
error: !!pair.measuresSpec.required && !measureNames.length || !!pair.dimensionsSpec.required && !dimensionNames.length || required && !items.length,
|
|
1849
1924
|
onChange: (next) => {
|
|
1850
1925
|
const dimensionList = next.filter((item) => item.kind === "dimension");
|
|
1851
1926
|
const measureList = next.filter((item) => item.kind === "measure");
|
|
1852
|
-
const
|
|
1853
|
-
const
|
|
1927
|
+
const percentList = next.filter((item) => item.kind === "percent").map((i) => i.name);
|
|
1928
|
+
const names = next.map(itemColumn);
|
|
1929
|
+
const standard = tableColumns([...dimensionList, ...measureList].map((item) => item.name), percentList);
|
|
1854
1930
|
const changes = {
|
|
1855
1931
|
[pair.dimensions]: dimensionList.length || pair.dimensionsSpec.required ? dimensionList.map((item) => item.name) : void 0,
|
|
1856
1932
|
[pair.measures]: measureList.length || pair.measuresSpec.required ? measureList.map((item) => item.name) : void 0
|
|
1857
1933
|
};
|
|
1934
|
+
if (pair.percents) changes[pair.percents] = percentList.length ? percentList : void 0;
|
|
1858
1935
|
if (pair.order) changes[pair.order] = names.join("\0") === standard.join("\0") ? void 0 : names;
|
|
1859
1936
|
if (onSetMany) onSetMany(changes);
|
|
1860
1937
|
else for (const [key, value] of Object.entries(changes)) set(key, value);
|
|
@@ -1868,10 +1945,7 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1868
1945
|
case "measure":
|
|
1869
1946
|
case "measures": return measures;
|
|
1870
1947
|
case "formatter": return formatters.map((name) => ({ name }));
|
|
1871
|
-
case "column": return
|
|
1872
|
-
name,
|
|
1873
|
-
label: model.dimensions[name]?.label ?? model.measures[name]?.label
|
|
1874
|
-
}));
|
|
1948
|
+
case "column": return spec.outside ? fieldOptions : columnOptionsOf(columns);
|
|
1875
1949
|
case "enum": return spec.values.map((name) => ({
|
|
1876
1950
|
name,
|
|
1877
1951
|
label: spec.labels?.[name]
|
|
@@ -1958,6 +2032,13 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
1958
2032
|
}
|
|
1959
2033
|
});
|
|
1960
2034
|
}
|
|
2035
|
+
case "formatRules": return /* @__PURE__ */ jsx(FormatRulesInput, {
|
|
2036
|
+
rules: value ?? [],
|
|
2037
|
+
fieldOptions,
|
|
2038
|
+
columnOptions: columnOptionsOf(columns),
|
|
2039
|
+
model,
|
|
2040
|
+
onChange: (rules) => set(key, rules.length ? rules : void 0)
|
|
2041
|
+
});
|
|
1961
2042
|
case "formatters": {
|
|
1962
2043
|
const map = value ?? {};
|
|
1963
2044
|
if (!chosenDimensions.length) return /* @__PURE__ */ jsx(Typography, {
|
|
@@ -2019,14 +2100,17 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
2019
2100
|
};
|
|
2020
2101
|
const hidden = new Set([
|
|
2021
2102
|
merged?.measures,
|
|
2103
|
+
merged?.percents,
|
|
2022
2104
|
...entries.filter(([, spec]) => spec.kind === "columnOrder").map(([key]) => key),
|
|
2023
2105
|
labelsKey,
|
|
2024
2106
|
formatsKey,
|
|
2107
|
+
alignsKey,
|
|
2108
|
+
verticalKey,
|
|
2025
2109
|
formattersKey,
|
|
2026
2110
|
formatterKey
|
|
2027
2111
|
].filter(Boolean));
|
|
2028
2112
|
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";
|
|
2113
|
+
const fullRow = (spec) => spec.kind === "dimensions" || spec.kind === "measures" || spec.kind === "formatters" || spec.kind === "formatRules";
|
|
2030
2114
|
const gridColumn = (spec, key) => fullRow(spec) ? "1 / -1" : key === "title" && formColumns > 1 ? "span 2" : void 0;
|
|
2031
2115
|
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((section) => {
|
|
2032
2116
|
const inSection = entries.filter(([, spec]) => spec.section === section);
|
|
@@ -2040,12 +2124,351 @@ function FieldsForm({ fields, values, onSet: set, onSetMany, model, formatters,
|
|
|
2040
2124
|
gridColumn: gridColumn(spec, key),
|
|
2041
2125
|
alignSelf: spec.kind === "boolean" ? "end" : void 0
|
|
2042
2126
|
},
|
|
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. " : ""}${[
|
|
2127
|
+
children: spec.kind === "boolean" ? withHint(input(key, spec), spec.hint) : merged?.dimensions === key ? field(merged.percents ? "Dimensões, medidas e %" : "Dimensões e medidas", input(key, spec), `${merged.order ? "Em qualquer ordem: é a ordem das colunas. " : ""}${[
|
|
2128
|
+
spec.hint,
|
|
2129
|
+
merged.measuresSpec.hint,
|
|
2130
|
+
merged.percentsSpec?.hint && `% do total: ${merged.percentsSpec.hint}`
|
|
2131
|
+
].filter(Boolean).join(" · ")}`) : field(spec.label, input(key, spec), spec.hint)
|
|
2044
2132
|
}, key))]
|
|
2045
2133
|
})
|
|
2046
2134
|
}, section);
|
|
2047
2135
|
}) });
|
|
2048
2136
|
}
|
|
2137
|
+
const fieldLabelOf = (model, name) => model.dimensions[name]?.label ?? model.measures[name]?.label ?? name;
|
|
2138
|
+
/** Exemplo de célula com o estilo de uma regra. */
|
|
2139
|
+
const RULE_SAMPLE = "Exemplo";
|
|
2140
|
+
const DEFAULT_RULE_BACKGROUND = "#ffe3e3";
|
|
2141
|
+
/**
|
|
2142
|
+
* Regras de formatação condicional, uma por cartão: "se <campo> <operador> <valor>, pinta <a célula
|
|
2143
|
+
* de uma coluna | a linha toda> com <texto, fundo, negrito>". A ordem importa (a de baixo ganha):
|
|
2144
|
+
* reordena com ↑/↓.
|
|
2145
|
+
*/
|
|
2146
|
+
function FormatRulesInput({ rules, fieldOptions, columnOptions, model, onChange }) {
|
|
2147
|
+
const update = (index, change) => onChange(rules.map((rule, i) => i === index ? change(rule) : rule));
|
|
2148
|
+
const columns = columnOptions.map((option) => option.name);
|
|
2149
|
+
const parseValue = (rule, text) => {
|
|
2150
|
+
const trimmed = text.trim();
|
|
2151
|
+
if (!trimmed) return void 0;
|
|
2152
|
+
const number = Number(trimmed.replace(",", "."));
|
|
2153
|
+
if (NUMERIC_OPERATORS.includes(rule.operator)) return Number.isFinite(number) ? number : void 0;
|
|
2154
|
+
if (rule.operator === "contains") return trimmed;
|
|
2155
|
+
return !(model.dimensions[rule.field]?.type === "string") && Number.isFinite(number) ? number : trimmed;
|
|
2156
|
+
};
|
|
2157
|
+
const withOperator = (rule, operator) => {
|
|
2158
|
+
const { value, to, ...rest } = rule;
|
|
2159
|
+
if (UNARY_OPERATORS.includes(operator)) return {
|
|
2160
|
+
...rest,
|
|
2161
|
+
operator
|
|
2162
|
+
};
|
|
2163
|
+
const numeric = NUMERIC_OPERATORS.includes(operator);
|
|
2164
|
+
const next = {
|
|
2165
|
+
...rest,
|
|
2166
|
+
operator,
|
|
2167
|
+
value: numeric ? Number.isFinite(Number(value)) ? Number(value) : 0 : value ?? ""
|
|
2168
|
+
};
|
|
2169
|
+
if (operator === "between") next.to = to ?? next.value;
|
|
2170
|
+
return next;
|
|
2171
|
+
};
|
|
2172
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2173
|
+
sx: {
|
|
2174
|
+
display: "flex",
|
|
2175
|
+
flexDirection: "column",
|
|
2176
|
+
gap: .75,
|
|
2177
|
+
maxWidth: 760
|
|
2178
|
+
},
|
|
2179
|
+
children: [rules.map((rule, index) => {
|
|
2180
|
+
const painted = rule.target === "row" ? void 0 : rule.column ?? rule.field;
|
|
2181
|
+
const lost = painted !== void 0 && !columns.includes(painted);
|
|
2182
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
2183
|
+
sx: {
|
|
2184
|
+
border: 1,
|
|
2185
|
+
borderColor: "divider",
|
|
2186
|
+
borderRadius: 1,
|
|
2187
|
+
p: .75,
|
|
2188
|
+
display: "flex",
|
|
2189
|
+
flexDirection: "column",
|
|
2190
|
+
gap: .5,
|
|
2191
|
+
"&:hover .row-actions": { visibility: "visible" }
|
|
2192
|
+
},
|
|
2193
|
+
children: [
|
|
2194
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2195
|
+
sx: {
|
|
2196
|
+
display: "flex",
|
|
2197
|
+
gap: .5,
|
|
2198
|
+
alignItems: "center",
|
|
2199
|
+
flexWrap: "wrap"
|
|
2200
|
+
},
|
|
2201
|
+
children: [
|
|
2202
|
+
/* @__PURE__ */ jsxs(Typography, {
|
|
2203
|
+
sx: {
|
|
2204
|
+
fontSize: 12,
|
|
2205
|
+
color: "text.secondary"
|
|
2206
|
+
},
|
|
2207
|
+
children: [index + 1, ". Se"]
|
|
2208
|
+
}),
|
|
2209
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2210
|
+
sx: { flex: "2 1 140px" },
|
|
2211
|
+
children: /* @__PURE__ */ jsx(NameSelect, {
|
|
2212
|
+
value: rule.field,
|
|
2213
|
+
options: fieldOptions,
|
|
2214
|
+
required: true,
|
|
2215
|
+
onChange: (field) => update(index, (current) => ({
|
|
2216
|
+
...current,
|
|
2217
|
+
field
|
|
2218
|
+
}))
|
|
2219
|
+
})
|
|
2220
|
+
}),
|
|
2221
|
+
/* @__PURE__ */ jsx(TextField, {
|
|
2222
|
+
select: true,
|
|
2223
|
+
size: "small",
|
|
2224
|
+
value: rule.operator,
|
|
2225
|
+
onChange: (e) => update(index, (current) => withOperator(current, e.target.value)),
|
|
2226
|
+
sx: {
|
|
2227
|
+
...inputSx$1,
|
|
2228
|
+
flex: "1 1 110px"
|
|
2229
|
+
},
|
|
2230
|
+
children: RULE_OPERATORS.map((operator) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2231
|
+
value: operator,
|
|
2232
|
+
children: RULE_OPERATOR_LABEL[operator]
|
|
2233
|
+
}, operator))
|
|
2234
|
+
}),
|
|
2235
|
+
!UNARY_OPERATORS.includes(rule.operator) && /* @__PURE__ */ jsx(Box, {
|
|
2236
|
+
sx: { flex: "1 1 80px" },
|
|
2237
|
+
children: /* @__PURE__ */ jsx(CommitText, {
|
|
2238
|
+
value: rule.value === void 0 ? "" : String(rule.value),
|
|
2239
|
+
placeholder: "valor",
|
|
2240
|
+
error: rule.value === void 0,
|
|
2241
|
+
onCommit: (text) => update(index, (current) => ({
|
|
2242
|
+
...current,
|
|
2243
|
+
value: parseValue(current, text)
|
|
2244
|
+
}))
|
|
2245
|
+
})
|
|
2246
|
+
}),
|
|
2247
|
+
rule.operator === "between" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Typography, {
|
|
2248
|
+
sx: {
|
|
2249
|
+
fontSize: 12,
|
|
2250
|
+
color: "text.secondary"
|
|
2251
|
+
},
|
|
2252
|
+
children: "e"
|
|
2253
|
+
}), /* @__PURE__ */ jsx(Box, {
|
|
2254
|
+
sx: { flex: "1 1 80px" },
|
|
2255
|
+
children: /* @__PURE__ */ jsx(CommitText, {
|
|
2256
|
+
value: rule.to === void 0 ? "" : String(rule.to),
|
|
2257
|
+
placeholder: "até",
|
|
2258
|
+
error: rule.to === void 0,
|
|
2259
|
+
onCommit: (text) => update(index, (current) => {
|
|
2260
|
+
const to = parseValue(current, text);
|
|
2261
|
+
return {
|
|
2262
|
+
...current,
|
|
2263
|
+
to: typeof to === "number" ? to : void 0
|
|
2264
|
+
};
|
|
2265
|
+
})
|
|
2266
|
+
})
|
|
2267
|
+
})] }),
|
|
2268
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2269
|
+
className: "row-actions",
|
|
2270
|
+
sx: {
|
|
2271
|
+
display: "flex",
|
|
2272
|
+
visibility: "hidden"
|
|
2273
|
+
},
|
|
2274
|
+
children: [
|
|
2275
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2276
|
+
title: "Subir",
|
|
2277
|
+
disabled: index === 0,
|
|
2278
|
+
onClick: () => onChange(moveTo(rules, index, index - 1)),
|
|
2279
|
+
children: "↑"
|
|
2280
|
+
}),
|
|
2281
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2282
|
+
title: "Descer",
|
|
2283
|
+
disabled: index === rules.length - 1,
|
|
2284
|
+
onClick: () => onChange(moveTo(rules, index, index + 2)),
|
|
2285
|
+
children: "↓"
|
|
2286
|
+
}),
|
|
2287
|
+
/* @__PURE__ */ jsx(SmallButton, {
|
|
2288
|
+
title: "Remover",
|
|
2289
|
+
onClick: () => onChange(rules.filter((_, i) => i !== index)),
|
|
2290
|
+
children: "×"
|
|
2291
|
+
})
|
|
2292
|
+
]
|
|
2293
|
+
})
|
|
2294
|
+
]
|
|
2295
|
+
}),
|
|
2296
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2297
|
+
sx: {
|
|
2298
|
+
display: "flex",
|
|
2299
|
+
gap: .5,
|
|
2300
|
+
alignItems: "center",
|
|
2301
|
+
flexWrap: "wrap"
|
|
2302
|
+
},
|
|
2303
|
+
children: [
|
|
2304
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
2305
|
+
sx: {
|
|
2306
|
+
fontSize: 12,
|
|
2307
|
+
color: "text.secondary"
|
|
2308
|
+
},
|
|
2309
|
+
children: "pinta"
|
|
2310
|
+
}),
|
|
2311
|
+
/* @__PURE__ */ jsxs(TextField, {
|
|
2312
|
+
select: true,
|
|
2313
|
+
size: "small",
|
|
2314
|
+
value: rule.target === "row" ? "row" : "cell",
|
|
2315
|
+
onChange: (e) => update(index, ({ target: _target, column: _column, ...rest }) => e.target.value === "row" ? {
|
|
2316
|
+
...rest,
|
|
2317
|
+
target: "row"
|
|
2318
|
+
} : rest),
|
|
2319
|
+
sx: {
|
|
2320
|
+
...inputSx$1,
|
|
2321
|
+
flex: "1 1 110px"
|
|
2322
|
+
},
|
|
2323
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2324
|
+
value: "cell",
|
|
2325
|
+
children: "a célula de"
|
|
2326
|
+
}), /* @__PURE__ */ jsx(MenuItem, {
|
|
2327
|
+
value: "row",
|
|
2328
|
+
children: "a linha toda"
|
|
2329
|
+
})]
|
|
2330
|
+
}),
|
|
2331
|
+
rule.target !== "row" && /* @__PURE__ */ jsx(Box, {
|
|
2332
|
+
sx: { flex: "2 1 140px" },
|
|
2333
|
+
children: /* @__PURE__ */ jsx(NameSelect, {
|
|
2334
|
+
value: rule.column ?? "",
|
|
2335
|
+
options: columnOptions,
|
|
2336
|
+
emptyLabel: `o próprio campo${columns.includes(rule.field) ? "" : " (fora do gráfico)"}`,
|
|
2337
|
+
onChange: (column) => update(index, (current) => withField(current, "column", column || void 0))
|
|
2338
|
+
})
|
|
2339
|
+
}),
|
|
2340
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2341
|
+
sx: {
|
|
2342
|
+
px: 1,
|
|
2343
|
+
py: .25,
|
|
2344
|
+
fontSize: 12,
|
|
2345
|
+
border: 1,
|
|
2346
|
+
borderColor: "divider",
|
|
2347
|
+
color: rule.color,
|
|
2348
|
+
bgcolor: rule.background,
|
|
2349
|
+
fontWeight: rule.bold ? 700 : void 0
|
|
2350
|
+
},
|
|
2351
|
+
children: RULE_SAMPLE
|
|
2352
|
+
})
|
|
2353
|
+
]
|
|
2354
|
+
}),
|
|
2355
|
+
lost && /* @__PURE__ */ jsxs(Typography, {
|
|
2356
|
+
sx: {
|
|
2357
|
+
fontSize: 11,
|
|
2358
|
+
color: "warning.main"
|
|
2359
|
+
},
|
|
2360
|
+
children: [
|
|
2361
|
+
"\"",
|
|
2362
|
+
painted,
|
|
2363
|
+
"\" não é coluna do gráfico: escolha a coluna a pintar ou a linha toda"
|
|
2364
|
+
]
|
|
2365
|
+
}),
|
|
2366
|
+
/* @__PURE__ */ jsxs(Box, {
|
|
2367
|
+
sx: {
|
|
2368
|
+
display: "grid",
|
|
2369
|
+
gap: .5,
|
|
2370
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))",
|
|
2371
|
+
alignItems: "end"
|
|
2372
|
+
},
|
|
2373
|
+
children: [
|
|
2374
|
+
field("Cor do texto", /* @__PURE__ */ jsx(ColorInput, {
|
|
2375
|
+
value: rule.color,
|
|
2376
|
+
fallback: "#000000",
|
|
2377
|
+
onCommit: (color) => update(index, (current) => withField(current, "color", color))
|
|
2378
|
+
})),
|
|
2379
|
+
field("Fundo", /* @__PURE__ */ jsx(ColorInput, {
|
|
2380
|
+
value: rule.background,
|
|
2381
|
+
fallback: "#ffffff",
|
|
2382
|
+
onCommit: (color) => update(index, (current) => withField(current, "background", color))
|
|
2383
|
+
})),
|
|
2384
|
+
field("Alinhamento", /* @__PURE__ */ jsxs(TextField, {
|
|
2385
|
+
select: true,
|
|
2386
|
+
size: "small",
|
|
2387
|
+
fullWidth: true,
|
|
2388
|
+
value: rule.align ?? "",
|
|
2389
|
+
onChange: (e) => update(index, (current) => withField(current, "align", e.target.value || void 0)),
|
|
2390
|
+
slotProps: { select: { displayEmpty: true } },
|
|
2391
|
+
sx: inputSx$1,
|
|
2392
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2393
|
+
value: "",
|
|
2394
|
+
children: /* @__PURE__ */ jsx("em", { children: "não muda" })
|
|
2395
|
+
}), COLUMN_ALIGNS.map((option) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2396
|
+
value: option,
|
|
2397
|
+
children: ALIGN_LABEL[option]
|
|
2398
|
+
}, option))]
|
|
2399
|
+
})),
|
|
2400
|
+
field("Vertical", /* @__PURE__ */ jsxs(TextField, {
|
|
2401
|
+
select: true,
|
|
2402
|
+
size: "small",
|
|
2403
|
+
fullWidth: true,
|
|
2404
|
+
value: rule.verticalAlign ?? "",
|
|
2405
|
+
onChange: (e) => update(index, (current) => withField(current, "verticalAlign", e.target.value || void 0)),
|
|
2406
|
+
slotProps: { select: { displayEmpty: true } },
|
|
2407
|
+
sx: inputSx$1,
|
|
2408
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2409
|
+
value: "",
|
|
2410
|
+
children: /* @__PURE__ */ jsx("em", { children: "não muda" })
|
|
2411
|
+
}), COLUMN_VERTICAL_ALIGNS.map((option) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2412
|
+
value: option,
|
|
2413
|
+
children: VERTICAL_ALIGN_LABEL[option]
|
|
2414
|
+
}, option))]
|
|
2415
|
+
})),
|
|
2416
|
+
/* @__PURE__ */ jsx(FormControlLabel, {
|
|
2417
|
+
control: /* @__PURE__ */ jsx(Checkbox, {
|
|
2418
|
+
size: "small",
|
|
2419
|
+
checked: !!rule.bold,
|
|
2420
|
+
onChange: (e) => update(index, (current) => withField(current, "bold", e.target.checked || void 0))
|
|
2421
|
+
}),
|
|
2422
|
+
label: /* @__PURE__ */ jsx(Typography, {
|
|
2423
|
+
sx: { fontSize: 12 },
|
|
2424
|
+
children: "Negrito"
|
|
2425
|
+
})
|
|
2426
|
+
})
|
|
2427
|
+
]
|
|
2428
|
+
})
|
|
2429
|
+
]
|
|
2430
|
+
}, index);
|
|
2431
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2432
|
+
size: "small",
|
|
2433
|
+
variant: "outlined",
|
|
2434
|
+
disabled: !fieldOptions.length,
|
|
2435
|
+
onClick: () => onChange([...rules, {
|
|
2436
|
+
field: columns.find((name) => !name.startsWith("%:")) ?? "",
|
|
2437
|
+
operator: ">",
|
|
2438
|
+
value: 0,
|
|
2439
|
+
background: DEFAULT_RULE_BACKGROUND
|
|
2440
|
+
}]),
|
|
2441
|
+
sx: {
|
|
2442
|
+
alignSelf: "flex-start",
|
|
2443
|
+
textTransform: "none"
|
|
2444
|
+
},
|
|
2445
|
+
children: "+ regra"
|
|
2446
|
+
})]
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
/** Campo de texto que só manda o valor no Enter ou ao sair (número parcial não vira valor). */
|
|
2450
|
+
function CommitText({ value, placeholder, error, onCommit }) {
|
|
2451
|
+
const [typing, setTyping] = useState(null);
|
|
2452
|
+
const commit = () => {
|
|
2453
|
+
if (typing === null) return;
|
|
2454
|
+
setTyping(null);
|
|
2455
|
+
if (typing !== value) onCommit(typing);
|
|
2456
|
+
};
|
|
2457
|
+
return /* @__PURE__ */ jsx(TextField, {
|
|
2458
|
+
size: "small",
|
|
2459
|
+
fullWidth: true,
|
|
2460
|
+
value: typing ?? value,
|
|
2461
|
+
placeholder,
|
|
2462
|
+
error: error && typing === null,
|
|
2463
|
+
onChange: (e) => setTyping(e.target.value),
|
|
2464
|
+
onBlur: commit,
|
|
2465
|
+
onKeyDown: (e) => {
|
|
2466
|
+
if (e.key === "Enter") commit();
|
|
2467
|
+
if (e.key === "Escape") setTyping(null);
|
|
2468
|
+
},
|
|
2469
|
+
sx: inputSx$1
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2049
2472
|
/**
|
|
2050
2473
|
* Cor com o seletor nativo. Mexer no seletor só muda a amostra: o valor vai para o editor quando
|
|
2051
2474
|
* ele fecha — o `change` nativo; o `onChange` do React dispara a cada movimento. O texto entra no
|
|
@@ -2115,6 +2538,16 @@ function pruned(map, keep) {
|
|
|
2115
2538
|
const next = Object.fromEntries(Object.entries(map).filter(([key]) => keep.includes(key)));
|
|
2116
2539
|
return Object.keys(next).length ? next : void 0;
|
|
2117
2540
|
}
|
|
2541
|
+
const ALIGN_LABEL = {
|
|
2542
|
+
left: "à esquerda",
|
|
2543
|
+
center: "centralizado",
|
|
2544
|
+
right: "à direita"
|
|
2545
|
+
};
|
|
2546
|
+
const VERTICAL_ALIGN_LABEL = {
|
|
2547
|
+
top: "em cima",
|
|
2548
|
+
middle: "no meio",
|
|
2549
|
+
bottom: "embaixo"
|
|
2550
|
+
};
|
|
2118
2551
|
const FORMAT_LABEL = {
|
|
2119
2552
|
number: "número",
|
|
2120
2553
|
integer: "inteiro",
|
|
@@ -2128,11 +2561,11 @@ const SAMPLE = 1234567.891;
|
|
|
2128
2561
|
* (dimensão) e formato (medida). Vazio = o do dicionário do modelo (que aparece como placeholder).
|
|
2129
2562
|
* Opção ausente (`undefined`) = o tipo do gráfico não tem esse campo.
|
|
2130
2563
|
*/
|
|
2131
|
-
function ColumnOptions({ kind, name, model, label, onLabel, format, onFormat, formatter, formatters, onFormatter, onReset }) {
|
|
2564
|
+
function ColumnOptions({ kind, name, model, label, onLabel, format, onFormat, formatter, formatters, onFormatter, align, defaultAlign, onAlign, verticalAlign, onVerticalAlign, onReset }) {
|
|
2132
2565
|
const [anchor, setAnchor] = useState(null);
|
|
2133
2566
|
const fromModel = kind === "measure" ? model.measures[name] : model.dimensions[name];
|
|
2134
2567
|
const measureMeta = kind === "measure" ? model.measures[name] : void 0;
|
|
2135
|
-
const customized = !!label || !!formatter || format !== void 0 && Object.keys(format).length > 0;
|
|
2568
|
+
const customized = !!label || !!formatter || !!align || !!verticalAlign || format !== void 0 && Object.keys(format).length > 0;
|
|
2136
2569
|
const setFormat = (key, next) => onFormat(withField(format ?? {}, key, next));
|
|
2137
2570
|
const decimals = format?.decimals;
|
|
2138
2571
|
const resolved = {
|
|
@@ -2223,6 +2656,46 @@ function ColumnOptions({ kind, name, model, label, onLabel, format, onFormat, fo
|
|
|
2223
2656
|
onChange: (e) => onLabel(e.target.value),
|
|
2224
2657
|
sx: inputSx$1
|
|
2225
2658
|
}), "Legenda, tooltip e cabeçalho, só neste gráfico; vazio = rótulo do dicionário"),
|
|
2659
|
+
align !== void 0 && field("Alinhamento", /* @__PURE__ */ jsxs(TextField, {
|
|
2660
|
+
select: true,
|
|
2661
|
+
size: "small",
|
|
2662
|
+
fullWidth: true,
|
|
2663
|
+
value: align,
|
|
2664
|
+
onChange: (e) => onAlign(e.target.value || void 0),
|
|
2665
|
+
slotProps: { select: { displayEmpty: true } },
|
|
2666
|
+
sx: small,
|
|
2667
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2668
|
+
value: "",
|
|
2669
|
+
children: /* @__PURE__ */ jsxs("em", { children: [
|
|
2670
|
+
"padrão (",
|
|
2671
|
+
ALIGN_LABEL[defaultAlign],
|
|
2672
|
+
")"
|
|
2673
|
+
] })
|
|
2674
|
+
}), COLUMN_ALIGNS.map((option) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2675
|
+
value: option,
|
|
2676
|
+
children: ALIGN_LABEL[option]
|
|
2677
|
+
}, option))]
|
|
2678
|
+
}), "Horizontal: cabeçalho, células e total da coluna"),
|
|
2679
|
+
verticalAlign !== void 0 && field("Alinhamento vertical", /* @__PURE__ */ jsxs(TextField, {
|
|
2680
|
+
select: true,
|
|
2681
|
+
size: "small",
|
|
2682
|
+
fullWidth: true,
|
|
2683
|
+
value: verticalAlign,
|
|
2684
|
+
onChange: (e) => onVerticalAlign(e.target.value || void 0),
|
|
2685
|
+
slotProps: { select: { displayEmpty: true } },
|
|
2686
|
+
sx: small,
|
|
2687
|
+
children: [/* @__PURE__ */ jsx(MenuItem, {
|
|
2688
|
+
value: "",
|
|
2689
|
+
children: /* @__PURE__ */ jsxs("em", { children: [
|
|
2690
|
+
"padrão (",
|
|
2691
|
+
VERTICAL_ALIGN_LABEL.middle,
|
|
2692
|
+
")"
|
|
2693
|
+
] })
|
|
2694
|
+
}), COLUMN_VERTICAL_ALIGNS.map((option) => /* @__PURE__ */ jsx(MenuItem, {
|
|
2695
|
+
value: option,
|
|
2696
|
+
children: VERTICAL_ALIGN_LABEL[option]
|
|
2697
|
+
}, option))]
|
|
2698
|
+
}), "Aparece quando a linha tem texto em mais de uma linha"),
|
|
2226
2699
|
formatter !== void 0 && field("Formatter", /* @__PURE__ */ jsx(NameSelect, {
|
|
2227
2700
|
value: formatter,
|
|
2228
2701
|
options: formatters.map((option) => ({ name: option })),
|
|
@@ -2400,13 +2873,14 @@ function NameSelect({ value, options, required, emptyLabel, onChange }) {
|
|
|
2400
2873
|
value,
|
|
2401
2874
|
children: [value, " (não existe)"]
|
|
2402
2875
|
}),
|
|
2403
|
-
options.
|
|
2876
|
+
options.flatMap((option, index) => [...option.group && option.group !== options[index - 1]?.group ? [/* @__PURE__ */ jsx(ListSubheader, { children: option.group }, `h:${option.group}`)] : [], /* @__PURE__ */ jsx(MenuItem, {
|
|
2404
2877
|
value: option.name,
|
|
2405
2878
|
children: /* @__PURE__ */ jsx(OptionText, { option })
|
|
2406
|
-
}, option.name))
|
|
2879
|
+
}, option.name)])
|
|
2407
2880
|
]
|
|
2408
2881
|
});
|
|
2409
2882
|
}
|
|
2883
|
+
const itemColumn = (item) => item.kind === "percent" ? percentColumn(item.name) : item.name;
|
|
2410
2884
|
const KIND_STYLE = {
|
|
2411
2885
|
dimension: {
|
|
2412
2886
|
icon: "◇",
|
|
@@ -2419,6 +2893,12 @@ const KIND_STYLE = {
|
|
|
2419
2893
|
color: "#F58220",
|
|
2420
2894
|
noun: "medida",
|
|
2421
2895
|
group: "Medidas"
|
|
2896
|
+
},
|
|
2897
|
+
percent: {
|
|
2898
|
+
icon: "%",
|
|
2899
|
+
color: "#9775fa",
|
|
2900
|
+
noun: "% do total",
|
|
2901
|
+
group: "% do total (da medida)"
|
|
2422
2902
|
}
|
|
2423
2903
|
};
|
|
2424
2904
|
/**
|
|
@@ -7631,4 +8111,4 @@ function ToggleButton$1({ draft, loading, problems, inspecting, onOpen }) {
|
|
|
7631
8111
|
//#endregion
|
|
7632
8112
|
export { DashboardDevtools as default };
|
|
7633
8113
|
|
|
7634
|
-
//# sourceMappingURL=DashboardDevtools-
|
|
8114
|
+
//# sourceMappingURL=DashboardDevtools-SCewfQGR.js.map
|