@bluemarble/bi 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { s as AGGREGATION_LABEL, t as canonicalJson, u as isAggregate } from "./codegen-DkfI6UJr.js";
2
- import { g as resolveChartTypes } from "./layout-nViz3MdU.js";
3
- import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
1
+ import { s as AGGREGATION_LABEL, t as canonicalJson, u as isAggregate } from "./codegen-LoQzFAle.js";
2
+ import { g as resolveChartTypes } from "./layout-CBzYDoqE.js";
3
+ import { Box, CircularProgress, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
4
4
  import { Suspense, createContext, lazy, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/devtools/layoutStore.ts
@@ -283,7 +283,7 @@ function createDevtoolsStore() {
283
283
  //#endregion
284
284
  //#region src/client/DashboardProvider.tsx
285
285
  const DashboardContext = createContext(null);
286
- const DashboardDevtools = lazy(() => import("./DashboardDevtools-CSRt9lib.js"));
286
+ const DashboardDevtools = lazy(() => import("./DashboardDevtools-CMIuUYpw.js"));
287
287
  const DEVTOOLS_DEFAULT = process.env.NODE_ENV === "development";
288
288
  const draftStorageKey = (model) => `bm-dashboard-devtools:draft:${model}`;
289
289
  function loadDraft(model) {
@@ -411,6 +411,123 @@ function useDashboard() {
411
411
  return context;
412
412
  }
413
413
  //#endregion
414
+ //#region src/client/chartKit.tsx
415
+ /**
416
+ * Peças comuns dos gráficos ligados ao modelo: paleta, filtro por clique, legenda e os estados de
417
+ * carregando/erro/vazio.
418
+ */
419
+ /**
420
+ * Paleta categórica, sempre nesta ordem (a série N tem sempre a mesma cor). Laranja Rocha primeiro;
421
+ * a ordem passa no validador de daltonismo (CVD ΔE ≥ 9 entre vizinhas) e de visão normal (≥ 19).
422
+ */
423
+ const SERIES_COLORS = [
424
+ "#F58220",
425
+ "#2a78d6",
426
+ "#1baf7a",
427
+ "#eda100",
428
+ "#e87ba4",
429
+ "#008300",
430
+ "#6250d6",
431
+ "#e34948"
432
+ ];
433
+ /** "Outros" (fatias agrupadas) fica neutro, fora da paleta. */
434
+ const OTHER_COLOR = "#9e9e9e";
435
+ const seriesColor = (index) => SERIES_COLORS[index % SERIES_COLORS.length];
436
+ /** Valor numérico de uma coluna da linha (`null` quando vazio). */
437
+ const numberOf = (row, key) => key && row[key] !== null && row[key] !== void 0 ? Number(row[key]) : null;
438
+ /**
439
+ * Filtro cruzado a partir do gráfico: clicar num valor filtra o dashboard por ele (Ctrl/Cmd+clique
440
+ * acumula; clicar de novo no único selecionado limpa). O gráfico deve ignorar o próprio filtro na
441
+ * consulta (`ignoreFilters`) e só destacar a seleção.
442
+ */
443
+ function useClickFilter(dimension, enabled) {
444
+ const { filters, setFilter } = useDashboard();
445
+ const selected = enabled && dimension ? filters[dimension] ?? [] : [];
446
+ const toggle = (value, event) => {
447
+ if (!enabled || !dimension) return;
448
+ if (event?.ctrlKey || event?.metaKey) setFilter(dimension, selected.includes(value) ? selected.filter((item) => item !== value) : [...selected, value]);
449
+ else setFilter(dimension, selected.length === 1 && selected[0] === value ? [] : [value]);
450
+ };
451
+ return {
452
+ selected,
453
+ /** Há seleção e este valor não está nela. */
454
+ isDimmed: (value) => selected.length > 0 && !selected.includes(value),
455
+ toggle,
456
+ clear: () => {
457
+ if (enabled && dimension && selected.length) setFilter(dimension, []);
458
+ }
459
+ };
460
+ }
461
+ function Legend({ items, loading }) {
462
+ return /* @__PURE__ */ jsxs(Box, {
463
+ sx: {
464
+ display: "flex",
465
+ flexWrap: "wrap",
466
+ columnGap: 1.5,
467
+ rowGap: .5,
468
+ mb: 1,
469
+ fontSize: 14,
470
+ color: "#555",
471
+ minHeight: 21
472
+ },
473
+ children: [items.map((item) => /* @__PURE__ */ jsxs(Box, {
474
+ sx: {
475
+ display: "flex",
476
+ alignItems: "center",
477
+ gap: .5
478
+ },
479
+ children: [/* @__PURE__ */ jsx(Box, { sx: {
480
+ width: 9,
481
+ height: 9,
482
+ borderRadius: "50%",
483
+ bgcolor: item.color
484
+ } }), item.label]
485
+ }, item.label)), loading && /* @__PURE__ */ jsx(CircularProgress, {
486
+ size: 14,
487
+ sx: { ml: "auto" }
488
+ })]
489
+ });
490
+ }
491
+ /** Erro / carregando / sem dados no lugar do gráfico; `null` quando há o que desenhar (função, não componente). */
492
+ function chartPlaceholder({ height, error, isLoading, empty }) {
493
+ if (!error && !empty) return null;
494
+ return /* @__PURE__ */ jsx(Box, {
495
+ sx: {
496
+ height,
497
+ display: "flex",
498
+ alignItems: "center",
499
+ justifyContent: "center",
500
+ color: error ? "error.main" : "#999"
501
+ },
502
+ children: error ? "Erro ao carregar o gráfico" : isLoading ? "Carregando…" : "Sem dados para os filtros selecionados"
503
+ });
504
+ }
505
+ /** `caption`: o que é o título (ex.: o rótulo da dimensão), em cinza acima dele. */
506
+ function TooltipBox({ title, caption, children }) {
507
+ return /* @__PURE__ */ jsxs(Box, {
508
+ sx: {
509
+ bgcolor: "#fff",
510
+ border: "1px solid #ccc",
511
+ p: 1,
512
+ fontSize: 13
513
+ },
514
+ children: [
515
+ caption && /* @__PURE__ */ jsx(Box, {
516
+ sx: {
517
+ fontSize: 11,
518
+ color: "#777"
519
+ },
520
+ children: caption
521
+ }),
522
+ /* @__PURE__ */ jsx(Box, {
523
+ sx: { fontWeight: 700 },
524
+ children: title
525
+ }),
526
+ children
527
+ ]
528
+ });
529
+ }
530
+ //#endregion
414
531
  //#region src/client/format.ts
415
532
  const formatters = /* @__PURE__ */ new Map();
416
533
  /** `compact`: notação curta do pt-BR (1,2 mil / 3,4 mi / 5,6 bi). */
@@ -624,6 +741,6 @@ function ModelDictionary() {
624
741
  });
625
742
  }
626
743
  //#endregion
627
- export { formatCurrency as a, measureFormat as c, useDashboard as d, errorMessage as f, parseLayoutItemKey as h, fieldLabel as i, measureFormatter as l, layoutItemKey as m, INSPECT_ATTRIBUTE as n, formatMeasure as o, LAYOUT_ITEM_ATTRIBUTE as p, useModelQuery as r, formatNumber as s, ModelDictionary as t, DashboardProvider as u };
744
+ export { parseLayoutItemKey as C, layoutItemKey as S, useClickFilter as _, formatCurrency as a, errorMessage as b, measureFormat as c, OTHER_COLOR as d, SERIES_COLORS as f, seriesColor as g, numberOf as h, fieldLabel as i, measureFormatter as l, chartPlaceholder as m, INSPECT_ATTRIBUTE as n, formatMeasure as o, TooltipBox as p, useModelQuery as r, formatNumber as s, ModelDictionary as t, Legend as u, DashboardProvider as v, LAYOUT_ITEM_ATTRIBUTE as x, useDashboard as y };
628
745
 
629
- //# sourceMappingURL=ModelDictionary-Dib4Vk3i.js.map
746
+ //# sourceMappingURL=ModelDictionary-CyeYU6ZJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ModelDictionary-CyeYU6ZJ.js","names":["loadDraft"],"sources":["../src/devtools/layoutStore.ts","../src/client/batchQueries.ts","../src/devtools/store.ts","../src/client/DashboardProvider.tsx","../src/client/chartKit.tsx","../src/client/format.ts","../src/client/useModelQuery.ts","../src/client/ModelDictionary.tsx"],"sourcesContent":["import { canonicalJson } from '../codegen'\nimport type { ChartsLayout } from '../layout'\n\n/**\n * Layouts montados na página (`<DashboardCharts>` / `<DashboardFilters>`) e o rascunho de cada um,\n * editado na aba Layout do devtools. Store externo como o de consultas: editar um gráfico\n * re-renderiza só os componentes daquele layout e o painel.\n */\n\n/** Parte do layout que um componente renderiza. */\nexport type LayoutPart = 'charts' | 'filters' | 'canvas'\n\nexport type LayoutEntry = {\n /** O layout que veio do arquivo (o que a página passou ao `<DashboardCharts>`). */\n registered: ChartsLayout\n /** Rascunho editado no devtools; `null` = página usando o registrado. */\n draft: ChartsLayout | null\n /**\n * Partes montadas na página e os formatters que cada uma recebeu (`formatDimension`,\n * `formatLabel`). Parte ausente = a página não renderiza (ex.: sem `<DashboardFilters>`).\n */\n parts: Partial<Record<LayoutPart, string[]>>\n}\n\n/**\n * Marca o elemento de cada item do layout com `<layoutId>/chart:<id>` ou\n * `<layoutId>/filter:<dimensão>` (destaque e inspetor do devtools).\n */\nexport const LAYOUT_ITEM_ATTRIBUTE = 'data-bm-dashboard-layout-item'\n\nexport const layoutItemKey = (layoutId: string, item: LayoutItem) =>\n `${layoutId}/${item.kind}:${item.key}`\n\nexport type LayoutItem = { kind: 'chart' | 'filter'; key: string }\n\nexport function parseLayoutItemKey(value: string): { layoutId: string; item: LayoutItem } | null {\n const match = /^([^/]+)\\/(chart|filter):(.+)$/.exec(value)\n return match\n ? {\n layoutId: match[1] as string,\n item: { kind: match[2] as LayoutItem['kind'], key: match[3] as string }\n }\n : null\n}\n\nconst draftKey = (id: string) => `bm-dashboard-devtools:layout:${id}`\n\nconst sameLayout = (a: ChartsLayout | null, b: ChartsLayout | null) =>\n canonicalJson(a) === canonicalJson(b)\n\nfunction loadDraft(id: string): ChartsLayout | null {\n try {\n const saved = sessionStorage.getItem(draftKey(id))\n return saved ? (JSON.parse(saved) as ChartsLayout) : null\n } catch {\n return null\n }\n}\n\nfunction storeDraft(id: string, draft: ChartsLayout | null) {\n try {\n if (draft) sessionStorage.setItem(draftKey(id), JSON.stringify(draft))\n else sessionStorage.removeItem(draftKey(id))\n } catch {}\n}\n\nexport function createLayoutStore() {\n const entries = new Map<string, LayoutEntry>()\n // Montagens por layout e parte (`<id>\\u0000<parte>`)\n const mounts = new Map<string, number>()\n let snapshot: LayoutEntry[] = []\n const listeners = new Set<() => void>()\n // Edições da aba Layout ainda não aplicadas: sobrevivem a trocar de aba (o painel desmonta)\n const pending = new Map<string, ChartsLayout>()\n\n const emit = () => {\n snapshot = [...entries.values()]\n for (const listener of listeners) listener()\n }\n\n const setDraft = (id: string, next: ChartsLayout | null) => {\n const entry = entries.get(id)\n if (!entry) return\n // Rascunho igual ao arquivo não é rascunho (ex.: desfez tudo à mão)\n const draft = next && !sameLayout(next, entry.registered) ? next : null\n storeDraft(id, draft)\n entries.set(id, { ...entry, draft })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n get: (id: string) => entries.get(id) ?? null,\n /** Rascunho do layout, mesmo antes de registrar (evita renderizar o registrado e trocar). */\n draftOf(layout: ChartsLayout) {\n const entry = entries.get(layout.id)\n if (entry) return entry.draft\n const draft = loadDraft(layout.id)\n return draft && !sameLayout(draft, layout) ? draft : null\n },\n\n /** Chamado pelo `<DashboardCharts>`/`<DashboardFilters>` ao montar e quando o arquivo muda (HMR). */\n register(layout: ChartsLayout, part: LayoutPart, formatters: string[]) {\n const current = entries.get(layout.id)\n let draft = current ? current.draft : loadDraft(layout.id)\n // Depois de \"Salvar no arquivo\" o Next recarrega o JSON: o rascunho virou o registrado\n if (draft && sameLayout(draft, layout)) {\n draft = null\n storeDraft(layout.id, null)\n }\n const parts = { ...current?.parts, [part]: formatters }\n entries.set(layout.id, { registered: layout, draft, parts })\n const mountKey = `${layout.id}\\u0000${part}`\n mounts.set(mountKey, (mounts.get(mountKey) ?? 0) + 1)\n emit()\n return () => {\n const count = (mounts.get(mountKey) ?? 1) - 1\n mounts.set(mountKey, count)\n const entry = entries.get(layout.id)\n if (count > 0 || !entry) return\n const { [part]: _, ...rest } = entry.parts\n // O rascunho continua no sessionStorage e volta na próxima montagem\n if (Object.keys(rest).length) entries.set(layout.id, { ...entry, parts: rest })\n else entries.delete(layout.id)\n emit()\n }\n },\n setDraft,\n /** Edições ainda não aplicadas do layout (só em memória). */\n pendingOf: (id: string) => pending.get(id) ?? null,\n setPending(id: string, layout: ChartsLayout | null) {\n if (layout) pending.set(id, layout)\n else pending.delete(id)\n }\n }\n}\n\nexport type LayoutStore = ReturnType<typeof createLayoutStore>\n","import type { DashboardQuery, DashboardResult } from '../types'\n\ntype Fetcher = (query: DashboardQuery) => Promise<DashboardResult>\n\ntype Pending = {\n query: DashboardQuery\n resolve: (result: DashboardResult) => void\n reject: (error: unknown) => void\n}\n\n/**\n * Manda num request só (`{ model, queries }`) as consultas de um modelo feitas no mesmo ciclo — as\n * de um render da página. O servidor junta numa SQL só as que dão para juntar (mesmas dimensões,\n * filtros e fatos; ver `runBatch`): os KPIs viram uma consulta com todas as medidas, e \"Vol. Total\"\n * (fórmula de \"Vol. Convencional\" + \"Vol. Esteira\") reaproveita as agregações dos outros dois.\n *\n * Consultas do devtools (`query.devtools`) e de metadados passam direto. Se o lote falhar (ou o\n * servidor for de uma versão sem lote), cada consulta é refeita sozinha.\n */\nexport function batchQueries(fetcher: Fetcher): Fetcher {\n const batches = new Map<string, Pending[]>()\n\n const alone = ({ query, resolve, reject }: Pending) => fetcher(query).then(resolve, reject)\n\n const flush = (model: string) => {\n const members = batches.get(model) ?? []\n batches.delete(model)\n if (members.length === 1) return alone(members[0])\n const queries = members.map(({ query: { dimensions, measures, filters } }) => ({\n dimensions,\n measures,\n filters\n }))\n fetcher({ model, queries }).then(\n ({ results }) => {\n if (results?.length !== members.length) return members.forEach(alone)\n members.forEach(({ resolve, reject }, index) => {\n const { error, ...result } = results[index]\n if (error) reject(new Error(error))\n else resolve(result)\n })\n },\n () => members.forEach(alone)\n )\n }\n\n return (query) => {\n if (query.meta || query.devtools || query.queries) return fetcher(query)\n return new Promise((resolve, reject) => {\n const members = batches.get(query.model)\n if (members) return void members.push({ query, resolve, reject })\n batches.set(query.model, [{ query, resolve, reject }])\n // As consultas de uma renderização disparam todas nos efeitos do mesmo commit\n setTimeout(() => flush(query.model), 0)\n })\n }\n}\n","import type { DashboardQuery, DashboardQueryDebug, DashboardRow } from '../types'\n\n/**\n * Registro das consultas feitas pelos componentes (`useModelQuery`), lido pelo painel do devtools.\n * É um store externo (`useSyncExternalStore`) para que registrar uma consulta não re-renderize a\n * página inteira — só o painel.\n */\n\nexport type RunStatus = 'loading' | 'success' | 'error' | 'cancelled'\n\nexport type QueryRun = {\n runId: number\n instanceId: string\n label: string\n query: DashboardQuery\n status: RunStatus\n startedAt: number\n /** Ida e volta medida no browser. */\n durationMs?: number\n rows?: DashboardRow[]\n error?: string\n debug?: DashboardQueryDebug\n /** Foi num request junto com outras consultas (quantas, contando ela); o `debug` é do conjunto. */\n batched?: number\n}\n\nexport type TrackedInstance = {\n instanceId: string\n label: string\n runs: number\n last: QueryRun\n /** Refeita muitas vezes em pouco tempo: sinal de re-render em loop (ex.: array novo a cada render). */\n storm: boolean\n}\n\nexport type DevtoolsSnapshot = {\n instances: TrackedInstance[]\n /** Execuções mais recentes primeiro. */\n history: QueryRun[]\n}\n\nconst MAX_HISTORY = 100\n/** `STORM_RUNS` execuções da mesma instância dentro de `STORM_WINDOW_MS` = consulta em loop. */\nconst STORM_RUNS = 6\nconst STORM_WINDOW_MS = 5000\n\nexport function errorMessage(error: unknown): string {\n const err = error as {\n response?: { data?: { error?: string; message?: string } }\n message?: string\n }\n return err?.response?.data?.error ?? err?.response?.data?.message ?? err?.message ?? String(error)\n}\n\nexport function createDevtoolsStore() {\n const instances = new Map<string, TrackedInstance>()\n let history: QueryRun[] = []\n // Execuções em andamento (o histórico é limitado e pode já ter descartado uma consulta lenta)\n const pending = new Map<number, QueryRun>()\n const recentStarts = new Map<string, number[]>()\n // Re-execuções pedidas pelo próprio devtools (refresh, aplicar rascunho) não contam como loop\n let expectedUntil = 0\n let snapshot: DevtoolsSnapshot = { instances: [], history: [] }\n let nextRunId = 1\n const listeners = new Set<() => void>()\n\n const emit = () => {\n snapshot = { instances: [...instances.values()], history }\n for (const listener of listeners) listener()\n }\n\n const update = (runId: number, patch: Partial<QueryRun>) => {\n const current = pending.get(runId)\n if (!current) return\n pending.delete(runId)\n const run = { ...current, ...patch, durationMs: Date.now() - current.startedAt }\n history = history.map((item) => (item.runId === runId ? run : item))\n const instance = instances.get(run.instanceId)\n if (instance?.last.runId === runId) instances.set(run.instanceId, { ...instance, last: run })\n emit()\n }\n\n return {\n subscribe(listener: () => void) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n getSnapshot: () => snapshot,\n\n start(instanceId: string, label: string, query: DashboardQuery) {\n const run: QueryRun = {\n runId: nextRunId++,\n instanceId,\n label,\n query,\n status: 'loading',\n startedAt: Date.now()\n }\n pending.set(run.runId, run)\n history = [run, ...history].slice(0, MAX_HISTORY)\n const previous = instances.get(instanceId)\n const expected = run.startedAt <= expectedUntil\n const starts = [\n ...(recentStarts.get(instanceId) ?? []),\n ...(expected ? [] : [run.startedAt])\n ].filter((time) => run.startedAt - time <= STORM_WINDOW_MS)\n recentStarts.set(instanceId, starts)\n const storm = previous?.storm || starts.length >= STORM_RUNS\n if (storm && !previous?.storm)\n console.warn(\n `[@bluemarble/bi] \"${label}\" executou ${starts.length}x em ${STORM_WINDOW_MS / 1000}s. ` +\n 'Confira se o componente não recria dimensions/measures/filtros a cada render.'\n )\n instances.set(instanceId, {\n instanceId,\n label,\n runs: (previous?.runs ?? 0) + 1,\n last: run,\n storm\n })\n emit()\n return run.runId\n },\n succeed: (runId: number, rows: DashboardRow[], debug?: DashboardQueryDebug, batched?: number) =>\n update(runId, { status: 'success', rows, debug, batched }),\n fail: (runId: number, error: unknown) =>\n update(runId, { status: 'error', error: errorMessage(error) }),\n /** Consulta descartada antes de responder (filtro mudou, componente desmontou). */\n cancel: (runId: number) => update(runId, { status: 'cancelled' }),\n\n remove(instanceId: string) {\n recentStarts.delete(instanceId)\n if (instances.delete(instanceId)) emit()\n },\n /** Avisa que todas as consultas vão refazer agora, de propósito. */\n expectRefetch() {\n expectedUntil = Date.now() + 500\n },\n /** Zera os alertas de loop (depois de corrigir o componente, sem recarregar a página). */\n resetStorms() {\n recentStarts.clear()\n for (const [id, instance] of instances) instances.set(id, { ...instance, storm: false })\n emit()\n },\n clearHistory() {\n history = []\n emit()\n }\n }\n}\n\nexport type DevtoolsStore = ReturnType<typeof createDevtoolsStore>\n","import {\n type ComponentType,\n createContext,\n lazy,\n type ReactNode,\n Suspense,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState\n} from 'react'\nimport { createLayoutStore, type LayoutStore } from '../devtools/layoutStore'\nimport { batchQueries } from './batchQueries'\nimport { createDevtoolsStore, type DevtoolsStore } from '../devtools/store'\nimport { type ChartTypeSpec, type ChartTypes, resolveChartTypes } from '../layout'\nimport type { ModelDef, ModelMeta } from '../model'\nimport type { DashboardFilters, DashboardQuery, DashboardResult, FilterValue } from '../types'\n\nexport type DashboardFetcher = (query: DashboardQuery) => Promise<DashboardResult>\n\n/**\n * Tipo de gráfico do app: os campos do JSON (`ChartTypeSpec`) e o componente que os renderiza. O\n * componente recebe os campos como props (com os `default` aplicados e formatters já resolvidos) e\n * `queryLabel` — ver `ChartComponentProps`.\n */\n// biome-ignore lint/suspicious/noExplicitAny: cada tipo tem as próprias props, vindas do JSON\nexport type ChartComponentSpec = ChartTypeSpec & { component: ComponentType<any> }\n\n/** Props de um componente registrado em `charts`: os campos do tipo + `queryLabel`. */\nexport type ChartComponentProps<Fields extends object = Record<string, unknown>> = Fields & {\n /** Nome da consulta no devtools (`<título> · <tipo>`): passe ao `useModelQuery({ label })`. */\n queryLabel: string\n}\n\n/** Só existe com o devtools ligado. */\nexport type DashboardDevtoolsContext = {\n store: DevtoolsStore\n /** Layouts da página (`<DashboardCharts>`/`<DashboardFilters>`) e os rascunhos da aba Layout. */\n layouts: LayoutStore\n /** Consulta o modelo registrado, ignorando o rascunho (comparações, snapshots). */\n rawFetcher: DashboardFetcher\n /** Rascunho do modelo aplicado: todas as consultas da página passam a usá-lo. */\n draft: ModelDef | null\n setDraft: (draft: ModelDef | null) => void\n /** Refaz todas as consultas da página. */\n refresh: () => void\n defaultFilters: DashboardFilters\n setFilters: (filters: DashboardFilters) => void\n}\n\ntype DashboardContextValue = {\n model: string\n filters: DashboardFilters\n setFilter: (dimension: string, values: FilterValue[]) => void\n fetcher: DashboardFetcher\n /** Dicionário do modelo (rótulos, descrições, formatos); `null` até carregar. */\n meta: ModelMeta | null\n /** Muda quando o devtools pede para refazer as consultas. */\n refreshKey: number\n /** Tipos de gráfico do layout: os da lib e os do app (`charts`). */\n chartTypes: ChartTypes\n /** Componentes dos tipos do app, por nome. */\n chartComponents: Record<string, ChartComponentSpec['component']>\n devtools: DashboardDevtoolsContext | null\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\n// Lazy: o painel não entra no bundle da página, só é baixado quando o devtools está ligado\nconst DashboardDevtools = lazy(() => import('../devtools/DashboardDevtools'))\n\nconst DEVTOOLS_DEFAULT = process.env.NODE_ENV === 'development'\n\nconst draftStorageKey = (model: string) => `bm-dashboard-devtools:draft:${model}`\n\nfunction loadDraft(model: string): ModelDef | null {\n try {\n const saved = sessionStorage.getItem(draftStorageKey(model))\n return saved ? (JSON.parse(saved) as ModelDef) : null\n } catch {\n return null\n }\n}\n\ntype DashboardProviderProps = {\n /** Nome do modelo no servidor (chave do registro de modelos do app). */\n model: string\n /** Como chamar o endpoint do app (ex.: `(q) => api.post('/dashboard/query', q).then((r) => r.data)`). */\n fetcher: DashboardFetcher\n defaultFilters?: DashboardFilters\n /**\n * Painel de desenvolvimento (debug das consultas, edição do modelo). Padrão: ligado só com\n * `NODE_ENV=development`. O endpoint também precisa de `runQuery(..., { devtools: true })`.\n */\n devtools?: boolean\n /**\n * Tipos de gráfico do app, usáveis no layout JSON (`\"type\": \"<nome>\"`) e no editor do devtools.\n * Declare fora do componente (referência estável). O servidor precisa dos mesmos campos em\n * `devtools.chartTypes` para salvar o layout.\n */\n charts?: Record<string, ChartComponentSpec>\n /**\n * Manda as consultas de um render num request só; o servidor junta numa SQL as que têm as mesmas\n * dimensões, filtros e fatos (ex.: os KPIs). Padrão: ligado.\n */\n batch?: boolean\n children: ReactNode\n}\n\n/**\n * Guarda os filtros do dashboard. Todo componente dentro dele (filtros, gráficos, tabelas) consulta o\n * mesmo modelo com os mesmos filtros — é o que faz um slicer filtrar a página inteira, como no BI.\n */\nexport function DashboardProvider({\n model,\n fetcher,\n defaultFilters = {},\n devtools = DEVTOOLS_DEFAULT,\n charts,\n batch = true,\n children\n}: DashboardProviderProps) {\n const chartTypes = useMemo(() => resolveChartTypes(charts), [charts])\n const chartComponents = useMemo(\n () =>\n Object.fromEntries(\n Object.entries(charts ?? {}).map(([name, spec]) => [name, spec.component])\n ),\n [charts]\n )\n const [filters, setFilters] = useState<DashboardFilters>(defaultFilters)\n const defaultsRef = useRef(defaultFilters)\n\n const setFilter = useCallback(\n (dimension: string, values: FilterValue[]) =>\n setFilters((current) => ({ ...current, [dimension]: values })),\n []\n )\n\n // --- devtools ---\n const store = useMemo(() => (devtools ? createDevtoolsStore() : null), [devtools])\n const layouts = useMemo(() => (devtools ? createLayoutStore() : null), [devtools])\n // O rascunho aplicado sobrevive ao reload da aba (útil enquanto se edita o modelo)\n const [draft, setDraftState] = useState<ModelDef | null>(() =>\n devtools && typeof window !== 'undefined' ? loadDraft(model) : null\n )\n const [refreshKey, setRefreshKey] = useState(0)\n const [mounted, setMounted] = useState(false)\n useEffect(() => setMounted(true), [])\n\n const setDraft = useCallback(\n (next: ModelDef | null) => {\n store?.expectRefetch()\n setDraftState(next)\n try {\n if (next) sessionStorage.setItem(draftStorageKey(model), JSON.stringify(next))\n else sessionStorage.removeItem(draftStorageKey(model))\n } catch {}\n },\n [model, store]\n )\n\n const refresh = useCallback(() => {\n store?.expectRefetch()\n setRefreshKey((key) => key + 1)\n }, [store])\n\n // Com o devtools ligado, toda consulta pede debug e, se houver, roda contra o rascunho\n const effectiveFetcher = useMemo<DashboardFetcher>(() => {\n if (!devtools) return fetcher\n return (query) =>\n fetcher({\n ...query,\n devtools: { debug: true, ...(draft && { model: draft }), ...query.devtools }\n })\n }, [fetcher, devtools, draft])\n const queryFetcher = useMemo(\n () => (batch ? batchQueries(effectiveFetcher) : effectiveFetcher),\n [batch, effectiveFetcher]\n )\n\n const rawFetcher = useMemo<DashboardFetcher>(\n () => (query) => fetcher({ ...query, devtools: { debug: true, ...query.devtools } }),\n [fetcher]\n )\n\n // Dicionário: segue o rascunho, para os rótulos editados aparecerem na hora\n const [meta, setMeta] = useState<ModelMeta | null>(null)\n useEffect(() => {\n let cancelado = false\n effectiveFetcher({ model, meta: true })\n .then((result) => !cancelado && setMeta(result.meta ?? null))\n .catch(() => !cancelado && setMeta(null))\n return () => {\n cancelado = true\n }\n }, [effectiveFetcher, model])\n\n const devtoolsValue = useMemo(\n () =>\n store &&\n layouts && {\n store,\n layouts,\n rawFetcher,\n draft,\n setDraft,\n refresh,\n defaultFilters: defaultsRef.current,\n setFilters\n },\n [store, layouts, rawFetcher, draft, setDraft, refresh]\n )\n\n const value = useMemo(\n () => ({\n model,\n filters,\n setFilter,\n fetcher: queryFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtools: devtoolsValue\n }),\n [\n model,\n filters,\n setFilter,\n queryFetcher,\n meta,\n refreshKey,\n chartTypes,\n chartComponents,\n devtoolsValue\n ]\n )\n\n return (\n <DashboardContext.Provider value={value}>\n {children}\n {/* Só depois de montar: o painel lê localStorage e não deve participar da hidratação */}\n {devtoolsValue && mounted && (\n <Suspense fallback={null}>\n <DashboardDevtools />\n </Suspense>\n )}\n </DashboardContext.Provider>\n )\n}\n\nexport function useDashboard() {\n const context = useContext(DashboardContext)\n if (!context) throw new Error('useDashboard precisa estar dentro de <DashboardProvider>')\n return context\n}\n","import { Box, CircularProgress } from '@mui/material'\nimport type { MouseEvent, ReactNode } from 'react'\nimport type { DashboardRow, FilterValue } from '../types'\nimport { useDashboard } from './DashboardProvider'\n\n/**\n * Peças comuns dos gráficos ligados ao modelo: paleta, filtro por clique, legenda e os estados de\n * carregando/erro/vazio.\n */\n\n/**\n * Paleta categórica, sempre nesta ordem (a série N tem sempre a mesma cor). Laranja Rocha primeiro;\n * a ordem passa no validador de daltonismo (CVD ΔE ≥ 9 entre vizinhas) e de visão normal (≥ 19).\n */\nexport const SERIES_COLORS = [\n '#F58220',\n '#2a78d6',\n '#1baf7a',\n '#eda100',\n '#e87ba4',\n '#008300',\n '#6250d6',\n '#e34948'\n] as const\n\n/** \"Outros\" (fatias agrupadas) fica neutro, fora da paleta. */\nexport const OTHER_COLOR = '#9e9e9e'\n\nexport const seriesColor = (index: number) => SERIES_COLORS[index % SERIES_COLORS.length] as string\n\n/** Valor numérico de uma coluna da linha (`null` quando vazio). */\nexport const numberOf = (row: DashboardRow, key?: string) =>\n key && row[key] !== null && row[key] !== undefined ? Number(row[key]) : null\n\n/**\n * Filtro cruzado a partir do gráfico: clicar num valor filtra o dashboard por ele (Ctrl/Cmd+clique\n * acumula; clicar de novo no único selecionado limpa). O gráfico deve ignorar o próprio filtro na\n * consulta (`ignoreFilters`) e só destacar a seleção.\n */\nexport function useClickFilter(dimension: string | undefined, enabled: boolean) {\n const { filters, setFilter } = useDashboard()\n const selected = enabled && dimension ? (filters[dimension] ?? []) : []\n\n const toggle = (value: FilterValue, event?: Pick<MouseEvent, 'ctrlKey' | 'metaKey'>) => {\n if (!enabled || !dimension) return\n if (event?.ctrlKey || event?.metaKey)\n setFilter(\n dimension,\n selected.includes(value) ? selected.filter((item) => item !== value) : [...selected, value]\n )\n else setFilter(dimension, selected.length === 1 && selected[0] === value ? [] : [value])\n }\n\n return {\n selected,\n /** Há seleção e este valor não está nela. */\n isDimmed: (value: FilterValue) => selected.length > 0 && !selected.includes(value),\n toggle,\n clear: () => {\n if (enabled && dimension && selected.length) setFilter(dimension, [])\n }\n }\n}\n\nexport function Legend({\n items,\n loading\n}: {\n items: { label: string; color: string }[]\n loading?: boolean\n}) {\n return (\n <Box\n sx={{\n display: 'flex',\n flexWrap: 'wrap',\n columnGap: 1.5,\n rowGap: 0.5,\n mb: 1,\n fontSize: 14,\n color: '#555',\n minHeight: 21\n }}\n >\n {items.map((item) => (\n <Box key={item.label} sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>\n <Box sx={{ width: 9, height: 9, borderRadius: '50%', bgcolor: item.color }} />\n {item.label}\n </Box>\n ))}\n {loading && <CircularProgress size={14} sx={{ ml: 'auto' }} />}\n </Box>\n )\n}\n\n/** Erro / carregando / sem dados no lugar do gráfico; `null` quando há o que desenhar (função, não componente). */\nexport function chartPlaceholder({\n height,\n error,\n isLoading,\n empty\n}: {\n height: number\n error: unknown\n isLoading: boolean\n empty: boolean\n}): ReactNode {\n if (!error && !empty) return null\n return (\n <Box\n sx={{\n height,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: error ? 'error.main' : '#999'\n }}\n >\n {error\n ? 'Erro ao carregar o gráfico'\n : isLoading\n ? 'Carregando…'\n : 'Sem dados para os filtros selecionados'}\n </Box>\n )\n}\n\n/** `caption`: o que é o título (ex.: o rótulo da dimensão), em cinza acima dele. */\nexport function TooltipBox({\n title,\n caption,\n children\n}: {\n title: string\n caption?: string\n children: ReactNode\n}) {\n return (\n <Box sx={{ bgcolor: '#fff', border: '1px solid #ccc', p: 1, fontSize: 13 }}>\n {caption && <Box sx={{ fontSize: 11, color: '#777' }}>{caption}</Box>}\n <Box sx={{ fontWeight: 700 }}>{title}</Box>\n {children}\n </Box>\n )\n}\n","import type { ChartNumberFormat } from '../layout'\nimport type { ModelMeta } from '../model'\n\nconst formatters = new Map<string, Intl.NumberFormat>()\n\n/** `compact`: notação curta do pt-BR (1,2 mil / 3,4 mi / 5,6 bi). */\nexport function formatNumber(\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) {\n if (value === null || value === undefined || value === '') return ''\n const number = Number(value)\n if (Number.isNaN(number)) return String(value)\n const key = `${decimals}:${compact}`\n let formatter = formatters.get(key)\n if (!formatter) {\n formatter = new Intl.NumberFormat('pt-BR', {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n ...(compact && { notation: 'compact' })\n })\n formatters.set(key, formatter)\n }\n return formatter.format(number)\n}\n\nexport const formatCurrency = (\n value: number | string | null | undefined,\n decimals = 2,\n compact = false\n) => {\n const text = formatNumber(value, decimals, compact)\n return text && `R$ ${text}`\n}\n\n/**\n * Formata pelo dicionário do modelo (`format`/`decimals` da medida) ou pelo formato do gráfico.\n * `fallbackDecimals` é o padrão do componente quando nenhum dos dois define `decimals`.\n */\nexport function formatMeasure(\n value: number | string | null | undefined,\n meta: ChartNumberFormat | undefined,\n fallbackDecimals = 2\n) {\n if (value === null || value === undefined || value === '') return ''\n const decimals = meta?.decimals ?? (meta?.format === 'integer' ? 0 : fallbackDecimals)\n const compact = meta?.compact ?? false\n let text: string\n switch (meta?.format) {\n case 'currency':\n text = formatCurrency(value, decimals, compact)\n break\n case 'percent': {\n const number = formatNumber(Number(value) * 100, decimals, compact)\n text = number && `${number}%`\n break\n }\n default:\n text = formatNumber(value, decimals, compact)\n }\n return text && `${meta?.prefix ?? ''}${text}${meta?.suffix ?? ''}`\n}\n\n/** Dicionário do modelo com o formato do gráfico por cima. */\nexport function measureFormat(\n meta: ModelMeta | null,\n measure: string,\n formats?: Record<string, ChartNumberFormat>\n): ChartNumberFormat | undefined {\n const fromModel = meta?.measures[measure]\n const fromChart = formats?.[measure]\n if (!fromChart) return fromModel\n return {\n ...(fromModel?.format !== undefined && { format: fromModel.format }),\n ...(fromModel?.decimals !== undefined && { decimals: fromModel.decimals }),\n ...fromChart\n }\n}\n\n/**\n * Formatter de uma medida: o `formats` do gráfico (se houver) por cima do dicionário do modelo; sem\n * `format`, número com `fallbackDecimals`.\n */\nexport function measureFormatter(\n meta: ModelMeta | null,\n measure: string,\n fallbackDecimals = 2,\n formats?: Record<string, ChartNumberFormat>\n): (value: number | string | null | undefined) => string {\n const format = measureFormat(meta, measure, formats)\n return (value) => formatMeasure(value, format, fallbackDecimals)\n}\n\n/** Rótulo de uma dimensão/medida: o `labels` do gráfico, o dicionário do modelo ou o nome. */\nexport function fieldLabel(\n meta: ModelMeta | null,\n name: string,\n labels?: Record<string, string>\n): string {\n return labels?.[name] || meta?.dimensions[name]?.label || meta?.measures[name]?.label || name\n}\n","import { useEffect, useId, useMemo, useState } from 'react'\nimport type { DashboardFilters, DashboardRow } from '../types'\nimport { useDashboard } from './DashboardProvider'\n\nexport const INSPECT_ATTRIBUTE = 'data-bm-dashboard-query'\n\ntype UseModelQueryParams = {\n dimensions?: string[]\n measures?: string[]\n /**\n * Filtros do dashboard que esta consulta NÃO deve respeitar. Um filtro ignora a si mesmo para\n * listar as opções (senão, ao escolher um navio, a lista mostraria só ele).\n */\n ignoreFilters?: string[]\n /** Nome da consulta no devtools (padrão: dimensões × medidas). */\n label?: string\n /** Não consulta (devolve `rows: []`); ex.: a linha de total de uma tabela que não mostra total. */\n skip?: boolean\n}\n\nexport function useModelQuery({\n dimensions,\n measures,\n ignoreFilters,\n label,\n skip = false\n}: UseModelQueryParams) {\n const { model, filters, fetcher, refreshKey, devtools } = useDashboard()\n const instanceId = useId()\n\n const applied: DashboardFilters = {}\n for (const [dimension, values] of Object.entries(filters)) {\n if (values.length && !ignoreFilters?.includes(dimension)) applied[dimension] = values\n }\n\n // Chave por valor: só refaz a consulta quando o que vai pro servidor muda (não a cada render,\n // nem quando muda um filtro que esta consulta ignora)\n const key = JSON.stringify([model, dimensions, measures, applied])\n const query = useMemo(() => ({ model, dimensions, measures, filters: applied }), [key])\n\n const [rows, setRows] = useState<DashboardRow[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<unknown>()\n\n const store = devtools?.store\n const debugLabel =\n label ?? [dimensions?.join(', '), measures?.join(', ')].filter(Boolean).join(' × ')\n\n useEffect(() => {\n if (skip) {\n setRows([])\n setIsLoading(false)\n return\n }\n let cancelado = false\n setIsLoading(true)\n const runId = store?.start(instanceId, debugLabel, query)\n fetcher(query)\n .then((result) => {\n if (cancelado) return\n setRows(result.rows)\n setError(undefined)\n if (runId) store?.succeed(runId, result.rows, result.devtools?.debug, result.batched)\n })\n .catch((err) => {\n if (cancelado) return\n setError(err)\n if (runId) store?.fail(runId, err)\n })\n .finally(() => !cancelado && setIsLoading(false))\n return () => {\n cancelado = true\n if (runId) store?.cancel(runId)\n }\n }, [fetcher, query, refreshKey, skip])\n\n useEffect(() => (store ? () => store.remove(instanceId) : undefined), [store, instanceId, skip])\n\n // Espalhar no elemento raiz do componente: é como o inspetor do devtools acha o componente na tela\n const inspectProps = store && !skip ? { [INSPECT_ATTRIBUTE]: instanceId } : {}\n\n return { rows, isLoading, error, inspectProps }\n}\n","import {\n Box,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TableRow,\n Typography\n} from '@mui/material'\nimport { AGGREGATION_LABEL, isAggregate, type ModelMeta, type ValueFormat } from '../model'\nimport { useDashboard } from './DashboardProvider'\n\nconst FORMAT_LABEL: Record<ValueFormat, string> = {\n number: 'Número',\n integer: 'Inteiro',\n currency: 'Moeda (R$)',\n percent: 'Percentual'\n}\n\n/** Como a medida é calculada, em termos de negócio (o SQL não sai do servidor). */\nfunction formula(meta: ModelMeta, name: string) {\n const measure = meta.measures[name]\n const label = (ref: string) => meta.measures[ref]?.label ?? ref\n if (measure.type === 'formula' && measure.refs?.length)\n return `Combina ${measure.refs.map(label).join(', ')}`\n if (isAggregate(measure)) return AGGREGATION_LABEL[measure.type]\n if (measure.type !== 'ratio' || !measure.numerator || !measure.denominator) return '—'\n return `${label(measure.numerator)} ÷ ${label(measure.denominator)}`\n}\n\n/**\n * Dicionário do modelo do `DashboardProvider`: o que cada filtro/eixo e cada número significa. É\n * montado a partir de `label`/`description` das dimensões e medidas — documentação que não\n * desatualiza, porque é a mesma definição que calcula os números.\n */\nexport function ModelDictionary() {\n const { meta } = useDashboard()\n if (!meta) return null\n\n const header = (labels: string[]) => (\n <TableHead>\n <TableRow>\n {labels.map((label) => (\n <TableCell key={label} sx={{ fontWeight: 700 }}>\n {label}\n </TableCell>\n ))}\n </TableRow>\n </TableHead>\n )\n\n return (\n <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Indicadores</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Indicador', 'Descrição', 'Cálculo', 'Formato'])}\n <TableBody>\n {Object.entries(meta.measures).map(([name, measure]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{measure.label ?? name}</TableCell>\n <TableCell>{measure.description ?? '—'}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{formula(meta, name)}</TableCell>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>\n {measure.format ? FORMAT_LABEL[measure.format] : '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n <Box>\n <Typography sx={{ fontWeight: 700, mb: 1 }}>Filtros e agrupamentos</Typography>\n <TableContainer>\n <Table size=\"small\">\n {header(['Campo', 'Descrição'])}\n <TableBody>\n {Object.entries(meta.dimensions).map(([name, dimension]) => (\n <TableRow key={name}>\n <TableCell sx={{ whiteSpace: 'nowrap' }}>{dimension.label ?? name}</TableCell>\n <TableCell>{dimension.description ?? '—'}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </TableContainer>\n </Box>\n </Box>\n )\n}\n"],"mappings":";;;;;;;;;;AA4BA,MAAa,wBAAwB;AAErC,MAAa,iBAAiB,UAAkB,SAC9C,GAAG,SAAS,GAAG,KAAK,KAAK,GAAG,KAAK;AAInC,SAAgB,mBAAmB,OAA8D;CAC/F,MAAM,QAAQ,iCAAiC,KAAK,KAAK;CACzD,OAAO,QACH;EACE,UAAU,MAAM;EAChB,MAAM;GAAE,MAAM,MAAM;GAA0B,KAAK,MAAM;EAAa;CACxE,IACA;AACN;AAEA,MAAM,YAAY,OAAe,gCAAgC;AAEjE,MAAM,cAAc,GAAwB,MAC1C,cAAc,CAAC,MAAM,cAAc,CAAC;AAEtC,SAASA,YAAU,IAAiC;CAClD,IAAI;EACF,MAAM,QAAQ,eAAe,QAAQ,SAAS,EAAE,CAAC;EACjD,OAAO,QAAS,KAAK,MAAM,KAAK,IAAqB;CACvD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,WAAW,IAAY,OAA4B;CAC1D,IAAI;EACF,IAAI,OAAO,eAAe,QAAQ,SAAS,EAAE,GAAG,KAAK,UAAU,KAAK,CAAC;OAChE,eAAe,WAAW,SAAS,EAAE,CAAC;CAC7C,QAAQ,CAAC;AACX;AAEA,SAAgB,oBAAoB;CAClC,MAAM,0BAAU,IAAI,IAAyB;CAE7C,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,WAA0B,CAAC;CAC/B,MAAM,4BAAY,IAAI,IAAgB;CAEtC,MAAM,0BAAU,IAAI,IAA0B;CAE9C,MAAM,aAAa;EACjB,WAAW,CAAC,GAAG,QAAQ,OAAO,CAAC;EAC/B,KAAK,MAAM,YAAY,WAAW,SAAS;CAC7C;CAEA,MAAM,YAAY,IAAY,SAA8B;EAC1D,MAAM,QAAQ,QAAQ,IAAI,EAAE;EAC5B,IAAI,CAAC,OAAO;EAEZ,MAAM,QAAQ,QAAQ,CAAC,WAAW,MAAM,MAAM,UAAU,IAAI,OAAO;EACnE,WAAW,IAAI,KAAK;EACpB,QAAQ,IAAI,IAAI;GAAE,GAAG;GAAO;EAAM,CAAC;EACnC,KAAK;CACP;CAEA,OAAO;EACL,UAAU,UAAsB;GAC9B,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;EACA,mBAAmB;EACnB,MAAM,OAAe,QAAQ,IAAI,EAAE,KAAK;;EAExC,QAAQ,QAAsB;GAC5B,MAAM,QAAQ,QAAQ,IAAI,OAAO,EAAE;GACnC,IAAI,OAAO,OAAO,MAAM;GACxB,MAAM,QAAQA,YAAU,OAAO,EAAE;GACjC,OAAO,SAAS,CAAC,WAAW,OAAO,MAAM,IAAI,QAAQ;EACvD;;EAGA,SAAS,QAAsB,MAAkB,YAAsB;GACrE,MAAM,UAAU,QAAQ,IAAI,OAAO,EAAE;GACrC,IAAI,QAAQ,UAAU,QAAQ,QAAQA,YAAU,OAAO,EAAE;GAEzD,IAAI,SAAS,WAAW,OAAO,MAAM,GAAG;IACtC,QAAQ;IACR,WAAW,OAAO,IAAI,IAAI;GAC5B;GACA,MAAM,QAAQ;IAAE,GAAG,SAAS;KAAQ,OAAO;GAAW;GACtD,QAAQ,IAAI,OAAO,IAAI;IAAE,YAAY;IAAQ;IAAO;GAAM,CAAC;GAC3D,MAAM,WAAW,GAAG,OAAO,GAAG,QAAQ;GACtC,OAAO,IAAI,WAAW,OAAO,IAAI,QAAQ,KAAK,KAAK,CAAC;GACpD,KAAK;GACL,aAAa;IACX,MAAM,SAAS,OAAO,IAAI,QAAQ,KAAK,KAAK;IAC5C,OAAO,IAAI,UAAU,KAAK;IAC1B,MAAM,QAAQ,QAAQ,IAAI,OAAO,EAAE;IACnC,IAAI,QAAQ,KAAK,CAAC,OAAO;IACzB,MAAM,GAAG,OAAO,GAAG,GAAG,SAAS,MAAM;IAErC,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ,QAAQ,IAAI,OAAO,IAAI;KAAE,GAAG;KAAO,OAAO;IAAK,CAAC;SACzE,QAAQ,OAAO,OAAO,EAAE;IAC7B,KAAK;GACP;EACF;EACA;;EAEA,YAAY,OAAe,QAAQ,IAAI,EAAE,KAAK;EAC9C,WAAW,IAAY,QAA6B;GAClD,IAAI,QAAQ,QAAQ,IAAI,IAAI,MAAM;QAC7B,QAAQ,OAAO,EAAE;EACxB;CACF;AACF;;;;;;;;;;;;ACxHA,SAAgB,aAAa,SAA2B;CACtD,MAAM,0BAAU,IAAI,IAAuB;CAE3C,MAAM,SAAS,EAAE,OAAO,SAAS,aAAsB,QAAQ,KAAK,CAAC,CAAC,KAAK,SAAS,MAAM;CAE1F,MAAM,SAAS,UAAkB;EAC/B,MAAM,UAAU,QAAQ,IAAI,KAAK,KAAK,CAAC;EACvC,QAAQ,OAAO,KAAK;EACpB,IAAI,QAAQ,WAAW,GAAG,OAAO,MAAM,QAAQ,EAAE;EAMjD,QAAQ;GAAE;GAAO,SALD,QAAQ,KAAK,EAAE,OAAO,EAAE,YAAY,UAAU,iBAAiB;IAC7E;IACA;IACA;GACF,EACuB;EAAE,CAAC,CAAC,CAAC,MACzB,EAAE,cAAc;GACf,IAAI,SAAS,WAAW,QAAQ,QAAQ,OAAO,QAAQ,QAAQ,KAAK;GACpE,QAAQ,SAAS,EAAE,SAAS,UAAU,UAAU;IAC9C,MAAM,EAAE,OAAO,GAAG,WAAW,QAAQ;IACrC,IAAI,OAAO,OAAO,IAAI,MAAM,KAAK,CAAC;SAC7B,QAAQ,MAAM;GACrB,CAAC;EACH,SACM,QAAQ,QAAQ,KAAK,CAC7B;CACF;CAEA,QAAQ,UAAU;EAChB,IAAI,MAAM,QAAQ,MAAM,YAAY,MAAM,SAAS,OAAO,QAAQ,KAAK;EACvE,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,UAAU,QAAQ,IAAI,MAAM,KAAK;GACvC,IAAI,SAAS,OAAO,KAAK,QAAQ,KAAK;IAAE;IAAO;IAAS;GAAO,CAAC;GAChE,QAAQ,IAAI,MAAM,OAAO,CAAC;IAAE;IAAO;IAAS;GAAO,CAAC,CAAC;GAErD,iBAAiB,MAAM,MAAM,KAAK,GAAG,CAAC;EACxC,CAAC;CACH;AACF;;;ACfA,MAAM,cAAc;;AAEpB,MAAM,aAAa;AACnB,MAAM,kBAAkB;AAExB,SAAgB,aAAa,OAAwB;CACnD,MAAM,MAAM;CAIZ,OAAO,KAAK,UAAU,MAAM,SAAS,KAAK,UAAU,MAAM,WAAW,KAAK,WAAW,OAAO,KAAK;AACnG;AAEA,SAAgB,sBAAsB;CACpC,MAAM,4BAAY,IAAI,IAA6B;CACnD,IAAI,UAAsB,CAAC;CAE3B,MAAM,0BAAU,IAAI,IAAsB;CAC1C,MAAM,+BAAe,IAAI,IAAsB;CAE/C,IAAI,gBAAgB;CACpB,IAAI,WAA6B;EAAE,WAAW,CAAC;EAAG,SAAS,CAAC;CAAE;CAC9D,IAAI,YAAY;CAChB,MAAM,4BAAY,IAAI,IAAgB;CAEtC,MAAM,aAAa;EACjB,WAAW;GAAE,WAAW,CAAC,GAAG,UAAU,OAAO,CAAC;GAAG;EAAQ;EACzD,KAAK,MAAM,YAAY,WAAW,SAAS;CAC7C;CAEA,MAAM,UAAU,OAAe,UAA6B;EAC1D,MAAM,UAAU,QAAQ,IAAI,KAAK;EACjC,IAAI,CAAC,SAAS;EACd,QAAQ,OAAO,KAAK;EACpB,MAAM,MAAM;GAAE,GAAG;GAAS,GAAG;GAAO,YAAY,KAAK,IAAI,IAAI,QAAQ;EAAU;EAC/E,UAAU,QAAQ,KAAK,SAAU,KAAK,UAAU,QAAQ,MAAM,IAAK;EACnE,MAAM,WAAW,UAAU,IAAI,IAAI,UAAU;EAC7C,IAAI,UAAU,KAAK,UAAU,OAAO,UAAU,IAAI,IAAI,YAAY;GAAE,GAAG;GAAU,MAAM;EAAI,CAAC;EAC5F,KAAK;CACP;CAEA,OAAO;EACL,UAAU,UAAsB;GAC9B,UAAU,IAAI,QAAQ;GACtB,aAAa,UAAU,OAAO,QAAQ;EACxC;EACA,mBAAmB;EAEnB,MAAM,YAAoB,OAAe,OAAuB;GAC9D,MAAM,MAAgB;IACpB,OAAO;IACP;IACA;IACA;IACA,QAAQ;IACR,WAAW,KAAK,IAAI;GACtB;GACA,QAAQ,IAAI,IAAI,OAAO,GAAG;GAC1B,UAAU,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,WAAW;GAChD,MAAM,WAAW,UAAU,IAAI,UAAU;GACzC,MAAM,WAAW,IAAI,aAAa;GAClC,MAAM,SAAS,CACb,GAAI,aAAa,IAAI,UAAU,KAAK,CAAC,GACrC,GAAI,WAAW,CAAC,IAAI,CAAC,IAAI,SAAS,CACpC,CAAC,CAAC,QAAQ,SAAS,IAAI,YAAY,QAAQ,eAAe;GAC1D,aAAa,IAAI,YAAY,MAAM;GACnC,MAAM,QAAQ,UAAU,SAAS,OAAO,UAAU;GAClD,IAAI,SAAS,CAAC,UAAU,OACtB,QAAQ,KACN,qBAAqB,MAAM,aAAa,OAAO,OAAO,OAAO,kBAAkB,IAAK,iFAEtF;GACF,UAAU,IAAI,YAAY;IACxB;IACA;IACA,OAAO,UAAU,QAAQ,KAAK;IAC9B,MAAM;IACN;GACF,CAAC;GACD,KAAK;GACL,OAAO,IAAI;EACb;EACA,UAAU,OAAe,MAAsB,OAA6B,YAC1E,OAAO,OAAO;GAAE,QAAQ;GAAW;GAAM;GAAO;EAAQ,CAAC;EAC3D,OAAO,OAAe,UACpB,OAAO,OAAO;GAAE,QAAQ;GAAS,OAAO,aAAa,KAAK;EAAE,CAAC;;EAE/D,SAAS,UAAkB,OAAO,OAAO,EAAE,QAAQ,YAAY,CAAC;EAEhE,OAAO,YAAoB;GACzB,aAAa,OAAO,UAAU;GAC9B,IAAI,UAAU,OAAO,UAAU,GAAG,KAAK;EACzC;;EAEA,gBAAgB;GACd,gBAAgB,KAAK,IAAI,IAAI;EAC/B;;EAEA,cAAc;GACZ,aAAa,MAAM;GACnB,KAAK,MAAM,CAAC,IAAI,aAAa,WAAW,UAAU,IAAI,IAAI;IAAE,GAAG;IAAU,OAAO;GAAM,CAAC;GACvF,KAAK;EACP;EACA,eAAe;GACb,UAAU,CAAC;GACX,KAAK;EACP;CACF;AACF;;;ACjFA,MAAM,mBAAmB,cAA4C,IAAI;AAGzE,MAAM,oBAAoB,WAAW,OAAO,kCAAgC;AAE5E,MAAM,mBAAmB,QAAQ,IAAI,aAAa;AAElD,MAAM,mBAAmB,UAAkB,+BAA+B;AAE1E,SAAS,UAAU,OAAgC;CACjD,IAAI;EACF,MAAM,QAAQ,eAAe,QAAQ,gBAAgB,KAAK,CAAC;EAC3D,OAAO,QAAS,KAAK,MAAM,KAAK,IAAiB;CACnD,QAAQ;EACN,OAAO;CACT;AACF;;;;;AA+BA,SAAgB,kBAAkB,EAChC,OACA,SACA,iBAAiB,CAAC,GAClB,WAAW,kBACX,QACA,QAAQ,MACR,YACyB;CACzB,MAAM,aAAa,cAAc,kBAAkB,MAAM,GAAG,CAAC,MAAM,CAAC;CACpE,MAAM,kBAAkB,cAEpB,OAAO,YACL,OAAO,QAAQ,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM,KAAK,SAAS,CAAC,CAC3E,GACF,CAAC,MAAM,CACT;CACA,MAAM,CAAC,SAAS,cAAc,SAA2B,cAAc;CACvE,MAAM,cAAc,OAAO,cAAc;CAEzC,MAAM,YAAY,aACf,WAAmB,WAClB,YAAY,aAAa;EAAE,GAAG;GAAU,YAAY;CAAO,EAAE,GAC/D,CAAC,CACH;CAGA,MAAM,QAAQ,cAAe,WAAW,oBAAoB,IAAI,MAAO,CAAC,QAAQ,CAAC;CACjF,MAAM,UAAU,cAAe,WAAW,kBAAkB,IAAI,MAAO,CAAC,QAAQ,CAAC;CAEjF,MAAM,CAAC,OAAO,iBAAiB,eAC7B,YAAY,OAAO,WAAW,cAAc,UAAU,KAAK,IAAI,IACjE;CACA,MAAM,CAAC,YAAY,iBAAiB,SAAS,CAAC;CAC9C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,gBAAgB,WAAW,IAAI,GAAG,CAAC,CAAC;CAEpC,MAAM,WAAW,aACd,SAA0B;EACzB,OAAO,cAAc;EACrB,cAAc,IAAI;EAClB,IAAI;GACF,IAAI,MAAM,eAAe,QAAQ,gBAAgB,KAAK,GAAG,KAAK,UAAU,IAAI,CAAC;QACxE,eAAe,WAAW,gBAAgB,KAAK,CAAC;EACvD,QAAQ,CAAC;CACX,GACA,CAAC,OAAO,KAAK,CACf;CAEA,MAAM,UAAU,kBAAkB;EAChC,OAAO,cAAc;EACrB,eAAe,QAAQ,MAAM,CAAC;CAChC,GAAG,CAAC,KAAK,CAAC;CAGV,MAAM,mBAAmB,cAAgC;EACvD,IAAI,CAAC,UAAU,OAAO;EACtB,QAAQ,UACN,QAAQ;GACN,GAAG;GACH,UAAU;IAAE,OAAO;IAAM,GAAI,SAAS,EAAE,OAAO,MAAM;IAAI,GAAG,MAAM;GAAS;EAC7E,CAAC;CACL,GAAG;EAAC;EAAS;EAAU;CAAK,CAAC;CAC7B,MAAM,eAAe,cACZ,QAAQ,aAAa,gBAAgB,IAAI,kBAChD,CAAC,OAAO,gBAAgB,CAC1B;CAEA,MAAM,aAAa,eACV,UAAU,QAAQ;EAAE,GAAG;EAAO,UAAU;GAAE,OAAO;GAAM,GAAG,MAAM;EAAS;CAAE,CAAC,GACnF,CAAC,OAAO,CACV;CAGA,MAAM,CAAC,MAAM,WAAW,SAA2B,IAAI;CACvD,gBAAgB;EACd,IAAI,YAAY;EAChB,iBAAiB;GAAE;GAAO,MAAM;EAAK,CAAC,CAAC,CACpC,MAAM,WAAW,CAAC,aAAa,QAAQ,OAAO,QAAQ,IAAI,CAAC,CAAC,CAC5D,YAAY,CAAC,aAAa,QAAQ,IAAI,CAAC;EAC1C,aAAa;GACX,YAAY;EACd;CACF,GAAG,CAAC,kBAAkB,KAAK,CAAC;CAE5B,MAAM,gBAAgB,cAElB,SACA,WAAW;EACT;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB,YAAY;EAC5B;CACF,GACF;EAAC;EAAO;EAAS;EAAY;EAAO;EAAU;CAAO,CACvD;CAEA,MAAM,QAAQ,eACL;EACL;EACA;EACA;EACA,SAAS;EACT;EACA;EACA;EACA;EACA,UAAU;CACZ,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,OACE,qBAAC,iBAAiB,UAAlB;EAAkC;EAAlC,UAAA,CACG,UAEA,iBAAiB,WAChB,oBAAC,UAAD;GAAU,UAAU;GAClB,UAAA,oBAAC,mBAAD,CAAoB,CAAA;EACZ,CAAA,CAEa;;AAE/B;AAEA,SAAgB,eAAe;CAC7B,MAAM,UAAU,WAAW,gBAAgB;CAC3C,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,0DAA0D;CACxF,OAAO;AACT;;;;;;;;;;;ACpPA,MAAa,gBAAgB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;AAGA,MAAa,cAAc;AAE3B,MAAa,eAAe,UAAkB,cAAc,QAAQ,cAAc;;AAGlF,MAAa,YAAY,KAAmB,QAC1C,OAAO,IAAI,SAAS,QAAQ,IAAI,SAAS,KAAA,IAAY,OAAO,IAAI,IAAI,IAAI;;;;;;AAO1E,SAAgB,eAAe,WAA+B,SAAkB;CAC9E,MAAM,EAAE,SAAS,cAAc,aAAa;CAC5C,MAAM,WAAW,WAAW,YAAa,QAAQ,cAAc,CAAC,IAAK,CAAC;CAEtE,MAAM,UAAU,OAAoB,UAAoD;EACtF,IAAI,CAAC,WAAW,CAAC,WAAW;EAC5B,IAAI,OAAO,WAAW,OAAO,SAC3B,UACE,WACA,SAAS,SAAS,KAAK,IAAI,SAAS,QAAQ,SAAS,SAAS,KAAK,IAAI,CAAC,GAAG,UAAU,KAAK,CAC5F;OACG,UAAU,WAAW,SAAS,WAAW,KAAK,SAAS,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;CACzF;CAEA,OAAO;EACL;;EAEA,WAAW,UAAuB,SAAS,SAAS,KAAK,CAAC,SAAS,SAAS,KAAK;EACjF;EACA,aAAa;GACX,IAAI,WAAW,aAAa,SAAS,QAAQ,UAAU,WAAW,CAAC,CAAC;EACtE;CACF;AACF;AAEA,SAAgB,OAAO,EACrB,OACA,WAIC;CACD,OACE,qBAAC,KAAD;EACE,IAAI;GACF,SAAS;GACT,UAAU;GACV,WAAW;GACX,QAAQ;GACR,IAAI;GACJ,UAAU;GACV,OAAO;GACP,WAAW;EACb;EAVF,UAAA,CAYG,MAAM,KAAK,SACV,qBAAC,KAAD;GAAsB,IAAI;IAAE,SAAS;IAAQ,YAAY;IAAU,KAAK;GAAI;GAA5E,UAAA,CACE,oBAAC,KAAD,EAAK,IAAI;IAAE,OAAO;IAAG,QAAQ;IAAG,cAAc;IAAO,SAAS,KAAK;GAAM,EAAI,CAAA,GAC5E,KAAK,KACH;EAHK,GAAA,KAAK,KAGV,CACN,GACA,WAAW,oBAAC,kBAAD;GAAkB,MAAM;GAAI,IAAI,EAAE,IAAI,OAAO;EAAI,CAAA,CAC1D;;AAET;;AAGA,SAAgB,iBAAiB,EAC/B,QACA,OACA,WACA,SAMY;CACZ,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO;CAC7B,OACE,oBAAC,KAAD;EACE,IAAI;GACF;GACA,SAAS;GACT,YAAY;GACZ,gBAAgB;GAChB,OAAO,QAAQ,eAAe;EAChC;EAEC,UAAA,QACG,+BACA,YACE,gBACA;CACH,CAAA;AAET;;AAGA,SAAgB,WAAW,EACzB,OACA,SACA,YAKC;CACD,OACE,qBAAC,KAAD;EAAK,IAAI;GAAE,SAAS;GAAQ,QAAQ;GAAkB,GAAG;GAAG,UAAU;EAAG;EAAzE,UAAA;GACG,WAAW,oBAAC,KAAD;IAAK,IAAI;KAAE,UAAU;KAAI,OAAO;IAAO;IAAI,UAAA;GAAa,CAAA;GACpE,oBAAC,KAAD;IAAK,IAAI,EAAE,YAAY,IAAI;IAAI,UAAA;GAAW,CAAA;GACzC;EACE;;AAET;;;AC7IA,MAAM,6BAAa,IAAI,IAA+B;;AAGtD,SAAgB,aACd,OACA,WAAW,GACX,UAAU,OACV;CACA,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IAAI,OAAO;CAClE,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,OAAO,MAAM,MAAM,GAAG,OAAO,OAAO,KAAK;CAC7C,MAAM,MAAM,GAAG,SAAS,GAAG;CAC3B,IAAI,YAAY,WAAW,IAAI,GAAG;CAClC,IAAI,CAAC,WAAW;EACd,YAAY,IAAI,KAAK,aAAa,SAAS;GACzC,uBAAuB;GACvB,uBAAuB;GACvB,GAAI,WAAW,EAAE,UAAU,UAAU;EACvC,CAAC;EACD,WAAW,IAAI,KAAK,SAAS;CAC/B;CACA,OAAO,UAAU,OAAO,MAAM;AAChC;AAEA,MAAa,kBACX,OACA,WAAW,GACX,UAAU,UACP;CACH,MAAM,OAAO,aAAa,OAAO,UAAU,OAAO;CAClD,OAAO,QAAQ,MAAM;AACvB;;;;;AAMA,SAAgB,cACd,OACA,MACA,mBAAmB,GACnB;CACA,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IAAI,OAAO;CAClE,MAAM,WAAW,MAAM,aAAa,MAAM,WAAW,YAAY,IAAI;CACrE,MAAM,UAAU,MAAM,WAAW;CACjC,IAAI;CACJ,QAAQ,MAAM,QAAd;EACE,KAAK;GACH,OAAO,eAAe,OAAO,UAAU,OAAO;GAC9C;EACF,KAAK,WAAW;GACd,MAAM,SAAS,aAAa,OAAO,KAAK,IAAI,KAAK,UAAU,OAAO;GAClE,OAAO,UAAU,GAAG,OAAO;GAC3B;EACF;EACA,SACE,OAAO,aAAa,OAAO,UAAU,OAAO;CAChD;CACA,OAAO,QAAQ,GAAG,MAAM,UAAU,KAAK,OAAO,MAAM,UAAU;AAChE;;AAGA,SAAgB,cACd,MACA,SACA,SAC+B;CAC/B,MAAM,YAAY,MAAM,SAAS;CACjC,MAAM,YAAY,UAAU;CAC5B,IAAI,CAAC,WAAW,OAAO;CACvB,OAAO;EACL,GAAI,WAAW,WAAW,KAAA,KAAa,EAAE,QAAQ,UAAU,OAAO;EAClE,GAAI,WAAW,aAAa,KAAA,KAAa,EAAE,UAAU,UAAU,SAAS;EACxE,GAAG;CACL;AACF;;;;;AAMA,SAAgB,iBACd,MACA,SACA,mBAAmB,GACnB,SACuD;CACvD,MAAM,SAAS,cAAc,MAAM,SAAS,OAAO;CACnD,QAAQ,UAAU,cAAc,OAAO,QAAQ,gBAAgB;AACjE;;AAGA,SAAgB,WACd,MACA,MACA,QACQ;CACR,OAAO,SAAS,SAAS,MAAM,WAAW,KAAK,EAAE,SAAS,MAAM,SAAS,KAAK,EAAE,SAAS;AAC3F;;;ACjGA,MAAa,oBAAoB;AAgBjC,SAAgB,cAAc,EAC5B,YACA,UACA,eACA,OACA,OAAO,SACe;CACtB,MAAM,EAAE,OAAO,SAAS,SAAS,YAAY,aAAa,aAAa;CACvE,MAAM,aAAa,MAAM;CAEzB,MAAM,UAA4B,CAAC;CACnC,KAAK,MAAM,CAAC,WAAW,WAAW,OAAO,QAAQ,OAAO,GACtD,IAAI,OAAO,UAAU,CAAC,eAAe,SAAS,SAAS,GAAG,QAAQ,aAAa;CAKjF,MAAM,MAAM,KAAK,UAAU;EAAC;EAAO;EAAY;EAAU;CAAO,CAAC;CACjE,MAAM,QAAQ,eAAe;EAAE;EAAO;EAAY;EAAU,SAAS;CAAQ,IAAI,CAAC,GAAG,CAAC;CAEtF,MAAM,CAAC,MAAM,WAAW,SAAyB,CAAC,CAAC;CACnD,MAAM,CAAC,WAAW,gBAAgB,SAAS,IAAI;CAC/C,MAAM,CAAC,OAAO,YAAY,SAAkB;CAE5C,MAAM,QAAQ,UAAU;CACxB,MAAM,aACJ,SAAS,CAAC,YAAY,KAAK,IAAI,GAAG,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CAEpF,gBAAgB;EACd,IAAI,MAAM;GACR,QAAQ,CAAC,CAAC;GACV,aAAa,KAAK;GAClB;EACF;EACA,IAAI,YAAY;EAChB,aAAa,IAAI;EACjB,MAAM,QAAQ,OAAO,MAAM,YAAY,YAAY,KAAK;EACxD,QAAQ,KAAK,CAAC,CACX,MAAM,WAAW;GAChB,IAAI,WAAW;GACf,QAAQ,OAAO,IAAI;GACnB,SAAS,KAAA,CAAS;GAClB,IAAI,OAAO,OAAO,QAAQ,OAAO,OAAO,MAAM,OAAO,UAAU,OAAO,OAAO,OAAO;EACtF,CAAC,CAAC,CACD,OAAO,QAAQ;GACd,IAAI,WAAW;GACf,SAAS,GAAG;GACZ,IAAI,OAAO,OAAO,KAAK,OAAO,GAAG;EACnC,CAAC,CAAC,CACD,cAAc,CAAC,aAAa,aAAa,KAAK,CAAC;EAClD,aAAa;GACX,YAAY;GACZ,IAAI,OAAO,OAAO,OAAO,KAAK;EAChC;CACF,GAAG;EAAC;EAAS;EAAO;EAAY;CAAI,CAAC;CAErC,gBAAiB,cAAc,MAAM,OAAO,UAAU,IAAI,KAAA,GAAY;EAAC;EAAO;EAAY;CAAI,CAAC;CAK/F,OAAO;EAAE;EAAM;EAAW;EAAO,cAFZ,SAAS,CAAC,OAAO,GAAG,oBAAoB,WAAW,IAAI,CAAC;CAE/B;AAChD;;;ACrEA,MAAM,eAA4C;CAChD,QAAQ;CACR,SAAS;CACT,UAAU;CACV,SAAS;AACX;;AAGA,SAAS,QAAQ,MAAiB,MAAc;CAC9C,MAAM,UAAU,KAAK,SAAS;CAC9B,MAAM,SAAS,QAAgB,KAAK,SAAS,IAAI,EAAE,SAAS;CAC5D,IAAI,QAAQ,SAAS,aAAa,QAAQ,MAAM,QAC9C,OAAO,WAAW,QAAQ,KAAK,IAAI,KAAK,CAAC,CAAC,KAAK,IAAI;CACrD,IAAI,YAAY,OAAO,GAAG,OAAO,kBAAkB,QAAQ;CAC3D,IAAI,QAAQ,SAAS,WAAW,CAAC,QAAQ,aAAa,CAAC,QAAQ,aAAa,OAAO;CACnF,OAAO,GAAG,MAAM,QAAQ,SAAS,EAAE,KAAK,MAAM,QAAQ,WAAW;AACnE;;;;;;AAOA,SAAgB,kBAAkB;CAChC,MAAM,EAAE,SAAS,aAAa;CAC9B,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,UAAU,WACd,oBAAC,WAAD,EAAA,UACE,oBAAC,UAAD,EAAA,UACG,OAAO,KAAK,UACX,oBAAC,WAAD;EAAuB,IAAI,EAAE,YAAY,IAAI;EAC1C,UAAA;CACQ,GAFK,KAEL,CACZ,EACO,CAAA,EACD,CAAA;CAGb,OACE,qBAAC,KAAD;EAAK,IAAI;GAAE,SAAS;GAAQ,eAAe;GAAU,KAAK;EAAE;EAA5D,UAAA,CACE,qBAAC,KAAD,EAAA,UAAA,CACE,oBAAC,YAAD;GAAY,IAAI;IAAE,YAAY;IAAK,IAAI;GAAE;GAAG,UAAA;EAAuB,CAAA,GACnE,oBAAC,gBAAD,EAAA,UACE,qBAAC,OAAD;GAAO,MAAK;GAAZ,UAAA,CACG,OAAO;IAAC;IAAa;IAAa;IAAW;GAAS,CAAC,GACxD,oBAAC,WAAD,EAAA,UACG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,MAAM,aACzC,qBAAC,UAAD,EAAA,UAAA;IACE,oBAAC,WAAD;KAAW,IAAI,EAAE,YAAY,SAAS;KAAI,UAAA,QAAQ,SAAS;IAAgB,CAAA;IAC3E,oBAAC,WAAD,EAAA,UAAY,QAAQ,eAAe,IAAe,CAAA;IAClD,oBAAC,WAAD;KAAW,IAAI,EAAE,YAAY,SAAS;KAAI,UAAA,QAAQ,MAAM,IAAI;IAAa,CAAA;IACzE,oBAAC,WAAD;KAAW,IAAI,EAAE,YAAY,SAAS;KACnC,UAAA,QAAQ,SAAS,aAAa,QAAQ,UAAU;IACxC,CAAA;GACH,EAAA,GAPK,IAOL,CACX,EACQ,CAAA,CACN;EACO,CAAA,EAAA,CAAA,CACb,EAAA,CAAA,GACL,qBAAC,KAAD,EAAA,UAAA,CACE,oBAAC,YAAD;GAAY,IAAI;IAAE,YAAY;IAAK,IAAI;GAAE;GAAG,UAAA;EAAkC,CAAA,GAC9E,oBAAC,gBAAD,EAAA,UACE,qBAAC,OAAD;GAAO,MAAK;GAAZ,UAAA,CACG,OAAO,CAAC,SAAS,WAAW,CAAC,GAC9B,oBAAC,WAAD,EAAA,UACG,OAAO,QAAQ,KAAK,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,eAC3C,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,WAAD;IAAW,IAAI,EAAE,YAAY,SAAS;IAAI,UAAA,UAAU,SAAS;GAAgB,CAAA,GAC7E,oBAAC,WAAD,EAAA,UAAY,UAAU,eAAe,IAAe,CAAA,CAC5C,EAAA,GAHK,IAGL,CACX,EACQ,CAAA,CACN;EACO,CAAA,EAAA,CAAA,CACb,EAAA,CAAA,CACF;;AAET"}
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as toModelCode, n as literal } from "./codegen-DkfI6UJr.js";
2
+ import { a as toModelCode, n as literal } from "./codegen-LoQzFAle.js";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { basename, dirname, join, relative, sep } from "node:path";
5
5
  import { parseArgs } from "node:util";
@@ -1,4 +1,4 @@
1
- import { A as DashboardQueryDebug, C as ColumnVerticalAlign, F as FilterValue, J as ModelDef, M as DashboardRow, O as DashboardFilters$1, S as ColumnAlign, Y as ModelMeta, d as ChartsLayout, i as CardStyle, j as DashboardResult, k as DashboardQuery, l as ChartTypeSpec, o as ChartDef, s as ChartNumberFormat, u as ChartTypes, w as FormatRule } from "../layout-CoGyGWYM.js";
1
+ import { A as DashboardQuery, C as ColumnAlign, I as FilterValue, M as DashboardResult, N as DashboardRow, T as FormatRule, X as ModelMeta, Y as ModelDef, a as CardStyle, c as ChartNumberFormat, d as ChartTypes, f as ChartsLayout, j as DashboardQueryDebug, k as DashboardFilters$1, n as BarValuePosition, s as ChartDef, u as ChartTypeSpec, w as ColumnVerticalAlign } from "../layout-DsG-zRIO.js";
2
2
  import { ComponentType, MouseEvent, ReactNode } from "react";
3
3
  //#region src/client/BarChart.d.ts
4
4
  type BarChartProps = {
@@ -26,8 +26,29 @@ type BarChartProps = {
26
26
  formatDimension?: (value: FilterValue) => string;
27
27
  /** Esconde categorias sem valor em nenhuma medida. */
28
28
  hideEmpty?: boolean;
29
- /** Valores nas pontas das barras (até 30 categorias; acima disso, só tooltip). */
29
+ /**
30
+ * Mostra todos os valores da dimensão no eixo, com ou sem dado nas medidas (zerados), na ordem
31
+ * da dimensão. Sobrepõe o `hideEmpty`.
32
+ */
33
+ showEmpty?: boolean;
34
+ /** Valores nas barras (até 30 categorias; acima disso, só tooltip). */
30
35
  showValues?: boolean;
36
+ /**
37
+ * Onde fica o valor: `outside` (na ponta, fora), `insideEnd` (dentro, na ponta), `center` ou
38
+ * `insideBase`. Empilhado, `outside` não mostra os pedaços (só o `showTotal`).
39
+ */
40
+ valuePosition?: BarValuePosition;
41
+ /** Cor do valor. Padrão: escuro fora da barra, branco dentro. */
42
+ valueColor?: string;
43
+ /** Tamanho do valor, em px. Padrão: 12. */
44
+ valueSize?: number;
45
+ /** Empilhado: total da pilha na ponta da barra. */
46
+ showTotal?: boolean;
47
+ /**
48
+ * Cor por série: `{ medida: '#rrggbb' }` ou, com `series`, `{ valor: '#rrggbb' }` (também
49
+ * "(em branco)" e "Outros"). O que faltar segue a paleta.
50
+ */
51
+ colors?: Record<string, string>;
31
52
  clickToFilter?: boolean;
32
53
  height?: number;
33
54
  /** Rótulo por dimensão/medida (sobrepõe o dicionário do modelo). */
@@ -43,7 +64,7 @@ type BarChartProps = {
43
64
  * Com `series`, uma medida quebrada pelos valores de outra dimensão:
44
65
  * `<BarChart dimension="mes" series="tipo_descarga" measures={['volume']} stacked />`.
45
66
  */
46
- declare function BarChart({ dimension, measures: measureList, series, formatSeries, maxSeries, orientation, stacked, sort, limit, formatDimension, hideEmpty, showValues, clickToFilter, height, labels, formats: formatsByMeasure, queryLabel }: BarChartProps): import("react").JSX.Element;
67
+ declare function BarChart({ dimension, measures: measureList, series, formatSeries, maxSeries, orientation, stacked, sort, limit, formatDimension, hideEmpty, showEmpty, showValues, valuePosition, valueColor, valueSize, showTotal, colors, clickToFilter, height, labels, formats: formatsByMeasure, queryLabel }: BarChartProps): import("react").JSX.Element;
47
68
  //#endregion
48
69
  //#region src/client/ComboChart.d.ts
49
70
  type ComboChartProps = {
@@ -1,126 +1,9 @@
1
- import { T as rowRuleStyles, _ as tableColumns, f as isBuiltinChart, h as percentColumn, o as chartCardStyle, u as customChartProps } from "../layout-nViz3MdU.js";
2
- import { a as formatCurrency, c as measureFormat, d as useDashboard, i as fieldLabel, l as measureFormatter, m as layoutItemKey, n as INSPECT_ATTRIBUTE, o as formatMeasure, p as LAYOUT_ITEM_ATTRIBUTE, r as useModelQuery, s as formatNumber, t as ModelDictionary, u as DashboardProvider } from "../ModelDictionary-Dib4Vk3i.js";
1
+ import { T as rowRuleStyles, _ as tableColumns, f as isBuiltinChart, h as percentColumn, o as chartCardStyle, u as customChartProps } from "../layout-CBzYDoqE.js";
2
+ import { S as layoutItemKey, _ as useClickFilter, a as formatCurrency, c as measureFormat, d as OTHER_COLOR, f as SERIES_COLORS, g as seriesColor, h as numberOf, i as fieldLabel, l as measureFormatter, m as chartPlaceholder, n as INSPECT_ATTRIBUTE, o as formatMeasure, p as TooltipBox, r as useModelQuery, s as formatNumber, t as ModelDictionary, u as Legend, v as DashboardProvider, x as LAYOUT_ITEM_ATTRIBUTE, y as useDashboard } from "../ModelDictionary-CyeYU6ZJ.js";
3
3
  import { Box, Checkbox, CircularProgress, FormControl, InputLabel, ListItemText, MenuItem, OutlinedInput, Paper, Select, Table, TableBody, TableCell, TableHead, TableRow, TableSortLabel, Typography } from "@mui/material";
4
4
  import { Bar, BarChart as BarChart$1, CartesianGrid, Cell, ComposedChart, LabelList, Line, Pie, PieChart, ResponsiveContainer, Tooltip as Tooltip$1, XAxis, YAxis } from "recharts";
5
5
  import { createContext, useContext, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- //#region src/client/chartKit.tsx
8
- /**
9
- * Peças comuns dos gráficos ligados ao modelo: paleta, filtro por clique, legenda e os estados de
10
- * carregando/erro/vazio.
11
- */
12
- /**
13
- * Paleta categórica, sempre nesta ordem (a série N tem sempre a mesma cor). Laranja Rocha primeiro;
14
- * a ordem passa no validador de daltonismo (CVD ΔE ≥ 9 entre vizinhas) e de visão normal (≥ 19).
15
- */
16
- const SERIES_COLORS = [
17
- "#F58220",
18
- "#2a78d6",
19
- "#1baf7a",
20
- "#eda100",
21
- "#e87ba4",
22
- "#008300",
23
- "#6250d6",
24
- "#e34948"
25
- ];
26
- /** "Outros" (fatias agrupadas) fica neutro, fora da paleta. */
27
- const OTHER_COLOR = "#9e9e9e";
28
- const seriesColor = (index) => SERIES_COLORS[index % SERIES_COLORS.length];
29
- /** Valor numérico de uma coluna da linha (`null` quando vazio). */
30
- const numberOf = (row, key) => key && row[key] !== null && row[key] !== void 0 ? Number(row[key]) : null;
31
- /**
32
- * Filtro cruzado a partir do gráfico: clicar num valor filtra o dashboard por ele (Ctrl/Cmd+clique
33
- * acumula; clicar de novo no único selecionado limpa). O gráfico deve ignorar o próprio filtro na
34
- * consulta (`ignoreFilters`) e só destacar a seleção.
35
- */
36
- function useClickFilter(dimension, enabled) {
37
- const { filters, setFilter } = useDashboard();
38
- const selected = enabled && dimension ? filters[dimension] ?? [] : [];
39
- const toggle = (value, event) => {
40
- if (!enabled || !dimension) return;
41
- if (event?.ctrlKey || event?.metaKey) setFilter(dimension, selected.includes(value) ? selected.filter((item) => item !== value) : [...selected, value]);
42
- else setFilter(dimension, selected.length === 1 && selected[0] === value ? [] : [value]);
43
- };
44
- return {
45
- selected,
46
- /** Há seleção e este valor não está nela. */
47
- isDimmed: (value) => selected.length > 0 && !selected.includes(value),
48
- toggle,
49
- clear: () => {
50
- if (enabled && dimension && selected.length) setFilter(dimension, []);
51
- }
52
- };
53
- }
54
- function Legend({ items, loading }) {
55
- return /* @__PURE__ */ jsxs(Box, {
56
- sx: {
57
- display: "flex",
58
- flexWrap: "wrap",
59
- columnGap: 1.5,
60
- rowGap: .5,
61
- mb: 1,
62
- fontSize: 14,
63
- color: "#555",
64
- minHeight: 21
65
- },
66
- children: [items.map((item) => /* @__PURE__ */ jsxs(Box, {
67
- sx: {
68
- display: "flex",
69
- alignItems: "center",
70
- gap: .5
71
- },
72
- children: [/* @__PURE__ */ jsx(Box, { sx: {
73
- width: 9,
74
- height: 9,
75
- borderRadius: "50%",
76
- bgcolor: item.color
77
- } }), item.label]
78
- }, item.label)), loading && /* @__PURE__ */ jsx(CircularProgress, {
79
- size: 14,
80
- sx: { ml: "auto" }
81
- })]
82
- });
83
- }
84
- /** Erro / carregando / sem dados no lugar do gráfico; `null` quando há o que desenhar (função, não componente). */
85
- function chartPlaceholder({ height, error, isLoading, empty }) {
86
- if (!error && !empty) return null;
87
- return /* @__PURE__ */ jsx(Box, {
88
- sx: {
89
- height,
90
- display: "flex",
91
- alignItems: "center",
92
- justifyContent: "center",
93
- color: error ? "error.main" : "#999"
94
- },
95
- children: error ? "Erro ao carregar o gráfico" : isLoading ? "Carregando…" : "Sem dados para os filtros selecionados"
96
- });
97
- }
98
- /** `caption`: o que é o título (ex.: o rótulo da dimensão), em cinza acima dele. */
99
- function TooltipBox({ title, caption, children }) {
100
- return /* @__PURE__ */ jsxs(Box, {
101
- sx: {
102
- bgcolor: "#fff",
103
- border: "1px solid #ccc",
104
- p: 1,
105
- fontSize: 13
106
- },
107
- children: [
108
- caption && /* @__PURE__ */ jsx(Box, {
109
- sx: {
110
- fontSize: 11,
111
- color: "#777"
112
- },
113
- children: caption
114
- }),
115
- /* @__PURE__ */ jsx(Box, {
116
- sx: { fontWeight: 700 },
117
- children: title
118
- }),
119
- children
120
- ]
121
- });
122
- }
123
- //#endregion
124
7
  //#region src/client/seriesPivot.ts
125
8
  const EMPTY_SERIES_LABEL = "(em branco)";
126
9
  const OTHERS$1 = "Outros";
@@ -197,9 +80,52 @@ function pivotSeries(rows, dimension, series, measure, items, format = String) {
197
80
  }
198
81
  /** Soma das séries de um ponto (a altura da barra empilhada). */
199
82
  const pointTotal = (point, items) => items.reduce((total, item) => total + Number(point[item.dataKey] ?? 0), 0);
83
+ /**
84
+ * Todas as categorias da dimensão (consulta só da dimensão, sem medidas), na ordem dela, com os
85
+ * valores dos pontos que vieram da consulta e zero nas que não têm dado (o "Mostrar itens sem
86
+ * dados" do Power BI). Ponto fora da lista (não deveria haver) vai no fim.
87
+ */
88
+ function fillCategories(points, categories, dimension, dataKeys, format = String) {
89
+ const byValue = new Map(points.map((point) => [String(point.value), point]));
90
+ const seen = /* @__PURE__ */ new Set();
91
+ const filled = [];
92
+ for (const row of categories) {
93
+ const value = row[dimension] ?? "";
94
+ const key = String(value);
95
+ if (seen.has(key)) continue;
96
+ seen.add(key);
97
+ const point = byValue.get(key);
98
+ if (point) filled.push(point);
99
+ else filled.push({
100
+ value,
101
+ label: format(value),
102
+ ...Object.fromEntries(dataKeys.map((dataKey) => [dataKey, 0]))
103
+ });
104
+ }
105
+ return [...filled, ...points.filter((point) => !seen.has(String(point.value)))];
106
+ }
200
107
  //#endregion
201
108
  //#region src/client/BarChart.tsx
202
109
  const MAX_VALUE_LABELS = 30;
110
+ /** Coluna do total da pilha no ponto (para o rótulo do `showTotal`). */
111
+ const TOTAL_KEY = "\0total";
112
+ /** Posição do valor no Recharts, conforme a orientação. */
113
+ const LABEL_POSITION = {
114
+ vertical: {
115
+ outside: "top",
116
+ insideEnd: "insideTop",
117
+ center: "center",
118
+ insideBase: "insideBottom"
119
+ },
120
+ horizontal: {
121
+ outside: "right",
122
+ insideEnd: "insideRight",
123
+ center: "center",
124
+ insideBase: "insideLeft"
125
+ }
126
+ };
127
+ /** Chave de uma série em `colors`: o valor, "(em branco)" ou "Outros". */
128
+ const seriesColorKey = (item) => item.others ? "Outros" : item.value === null ? EMPTY_SERIES_LABEL : String(item.value);
203
129
  /** Altura de cada categoria no modo horizontal (barras finas, com respiro). */
204
130
  const ROW_HEIGHT = 28;
205
131
  /**
@@ -208,7 +134,7 @@ const ROW_HEIGHT = 28;
208
134
  * Com `series`, uma medida quebrada pelos valores de outra dimensão:
209
135
  * `<BarChart dimension="mes" series="tipo_descarga" measures={['volume']} stacked />`.
210
136
  */
211
- function BarChart({ dimension, measures: measureList, series, formatSeries = String, maxSeries = 6, orientation = "vertical", stacked = false, sort = "dimension", limit, formatDimension = String, hideEmpty = true, showValues = true, clickToFilter = false, height = 360, labels, formats: formatsByMeasure, queryLabel }) {
137
+ function BarChart({ dimension, measures: measureList, series, formatSeries = String, maxSeries = 6, orientation = "vertical", stacked = false, sort = "dimension", limit, formatDimension = String, hideEmpty = true, showEmpty = false, showValues = true, valuePosition = "outside", valueColor, valueSize = 12, showTotal = false, colors, clickToFilter = false, height = 360, labels, formats: formatsByMeasure, queryLabel }) {
212
138
  const { meta, filters, setFilter } = useDashboard();
213
139
  const measures = series ? measureList.slice(0, 1) : measureList;
214
140
  const { rows, isLoading, error, inspectProps } = useModelQuery({
@@ -224,6 +150,13 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
224
150
  label: `${queryLabel ?? `BarChart · ${dimension}`} · séries`,
225
151
  skip: !series
226
152
  });
153
+ const table = meta?.dimensions[dimension]?.table;
154
+ const categories = useModelQuery({
155
+ dimensions: [dimension],
156
+ ignoreFilters: Object.keys(filters).filter((name) => clickToFilter && name === dimension || !table || meta?.dimensions[name]?.table !== table),
157
+ label: `${queryLabel ?? `BarChart · ${dimension}`} · categorias`,
158
+ skip: !showEmpty || !meta
159
+ });
227
160
  const filter = useClickFilter(dimension, clickToFilter);
228
161
  const seriesFilter = useClickFilter(series, clickToFilter);
229
162
  const formats = measures.map((measure) => measureFormatter(meta, measure, 0, formatsByMeasure));
@@ -241,6 +174,7 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
241
174
  const pivot = pivotSeries(rows, dimension, series, first, seriesItems(domain.rows, series, first, maxSeries, formatSeries), formatDimension);
242
175
  bars = pivot.items.map((item) => ({
243
176
  ...item,
177
+ color: colors?.[seriesColorKey(item)] ?? item.color,
244
178
  item,
245
179
  format
246
180
  }));
@@ -249,7 +183,7 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
249
183
  bars = measures.map((measure, index) => ({
250
184
  dataKey: measure,
251
185
  label: labelOf(measure),
252
- color: seriesColor(index),
186
+ color: colors?.[measure] ?? seriesColor(index),
253
187
  format: formats[index] ?? String
254
188
  }));
255
189
  data = rows.map((row) => {
@@ -262,18 +196,32 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
262
196
  return point;
263
197
  });
264
198
  }
265
- if (hideEmpty) data = data.filter((point) => bars.some((bar) => Number(point[bar.dataKey])));
199
+ if (showEmpty) data = fillCategories(data, categories.rows, dimension, bars.map((bar) => bar.dataKey), formatDimension);
200
+ else if (hideEmpty) data = data.filter((point) => bars.some((bar) => Number(point[bar.dataKey])));
266
201
  const sortValue = (point) => series ? pointTotal(point, bars.map((bar) => bar.item).filter((item) => !!item)) : Number(point[first ?? ""]);
267
202
  if (sort !== "dimension" && first) data = [...data].sort((a, b) => sort === "desc" ? sortValue(b) - sortValue(a) : sortValue(a) - sortValue(b));
268
203
  if (limit) data = data.slice(0, limit);
269
204
  const horizontal = orientation === "horizontal";
270
205
  const showLabels = showValues && data.length <= MAX_VALUE_LABELS;
206
+ const segmentLabels = showLabels && (!stacked || valuePosition !== "outside");
207
+ const totalLabel = showLabels && stacked && showTotal && bars.length > 0;
208
+ const labelsOutside = segmentLabels && valuePosition === "outside" || totalLabel;
209
+ if (totalLabel) data = data.map((point) => ({
210
+ ...point,
211
+ [TOTAL_KEY]: bars.reduce((total, bar) => total + Number(point[bar.dataKey] ?? 0), 0)
212
+ }));
213
+ const labelStyle = (outside) => ({
214
+ fontSize: valueSize,
215
+ fill: valueColor ?? (outside ? "#333" : "#fff"),
216
+ fontWeight: 600
217
+ });
218
+ const position = LABEL_POSITION[orientation === "horizontal" ? "horizontal" : "vertical"];
271
219
  const chartHeight = horizontal ? Math.max(height, data.length * ROW_HEIGHT + 24) : height;
272
220
  const longest = Math.max(0, ...data.map((point) => point.label.length));
273
- const loading = isLoading || domain.isLoading;
221
+ const loading = isLoading || domain.isLoading || categories.isLoading || showEmpty && !meta;
274
222
  const state = chartPlaceholder({
275
223
  height,
276
- error: error ?? domain.error,
224
+ error: error ?? domain.error ?? categories.error,
277
225
  isLoading: loading,
278
226
  empty: !data.length
279
227
  });
@@ -401,11 +349,11 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
401
349
  barCategoryGap: "20%",
402
350
  margin: horizontal ? {
403
351
  top: 4,
404
- right: showLabels ? 96 : 16,
352
+ right: labelsOutside ? 96 : 16,
405
353
  left: 8,
406
354
  bottom: 4
407
355
  } : {
408
- top: showLabels ? 24 : 8,
356
+ top: labelsOutside ? valueSize + 12 : 8,
409
357
  right: 8,
410
358
  left: 8,
411
359
  bottom: 0
@@ -467,16 +415,25 @@ function BarChart({ dimension, measures: measureList, series, formatSeries = Str
467
415
  isAnimationActive: false,
468
416
  onClick: clickToFilter ? (_bar, barIndex, event) => handleClick(barIndex, bar, event) : void 0,
469
417
  cursor: clickToFilter ? "pointer" : void 0,
470
- children: [clickToFilter && data.map((point) => /* @__PURE__ */ jsx(Cell, { fillOpacity: isDimmed(point, bar) ? .35 : 1 }, String(point.value))), showLabels && !stacked && /* @__PURE__ */ jsx(LabelList, {
471
- dataKey: bar.dataKey,
472
- position: horizontal ? "right" : "top",
473
- formatter: (value) => bar.format(value),
474
- style: {
475
- fontSize: 12,
476
- fill: "#333",
477
- fontWeight: 600
478
- }
479
- })]
418
+ children: [
419
+ clickToFilter && data.map((point) => /* @__PURE__ */ jsx(Cell, { fillOpacity: isDimmed(point, bar) ? .35 : 1 }, String(point.value))),
420
+ segmentLabels && /* @__PURE__ */ jsx(LabelList, {
421
+ dataKey: bar.dataKey,
422
+ position: position[valuePosition],
423
+ formatter: (value) => stacked && !Number(value) ? "" : bar.format(value),
424
+ style: labelStyle(valuePosition === "outside")
425
+ }),
426
+ totalLabel && index === bars.length - 1 && /* @__PURE__ */ jsx(LabelList, {
427
+ dataKey: TOTAL_KEY,
428
+ position: position.outside,
429
+ formatter: (value) => (bars[0]?.format ?? String)(value),
430
+ style: {
431
+ ...labelStyle(true),
432
+ ...valuePosition !== "outside" && { fill: "#333" },
433
+ fontWeight: 700
434
+ }
435
+ })
436
+ ]
480
437
  }, bar.dataKey))
481
438
  ]
482
439
  })