@squadbase/vantage 0.7.0 → 0.7.1-dev1.5e06644
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/README.md +1 -1
- package/dist/add-AU3EDPNW.js +6 -0
- package/dist/add-AU3EDPNW.js.map +1 -0
- package/dist/{chunk-AABMKHEU.js → chunk-35B3YIBF.js} +16 -7
- package/dist/chunk-35B3YIBF.js.map +1 -0
- package/dist/{chunk-TRMHNMMZ.js → chunk-LYUGDHSF.js} +3 -3
- package/dist/{chunk-TRMHNMMZ.js.map → chunk-LYUGDHSF.js.map} +1 -1
- package/dist/{chunk-QNOX7WLZ.js → chunk-MQUIAP66.js} +69 -4
- package/dist/chunk-MQUIAP66.js.map +1 -0
- package/dist/{chunk-2IAPIOPS.js → chunk-P2KIHF65.js} +2 -2
- package/dist/chunk-P2KIHF65.js.map +1 -0
- package/dist/{chunk-3RJEOP3J.js → chunk-XRKSIC5Z.js} +5 -3
- package/dist/chunk-XRKSIC5Z.js.map +1 -0
- package/dist/{chunk-77CISBMG.js → chunk-YJQCB3UP.js} +3387 -204
- package/dist/chunk-YJQCB3UP.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/components/index.d.ts +627 -7
- package/dist/components/index.js +4 -4
- package/dist/{delta-text-CbrOaW83.d.ts → delta-text-tav2Lh5d.d.ts} +1 -1
- package/dist/doc/index.d.ts +2 -2
- package/dist/doc/index.js +4 -4
- package/dist/{format-DihQkL1N.d.ts → format-D34piCei.d.ts} +2 -1
- package/dist/geo-japan-7BMSOJRX.js +8 -0
- package/dist/geo-japan-7BMSOJRX.js.map +1 -0
- package/dist/geo-us-GVOG7VXH.js +8 -0
- package/dist/geo-us-GVOG7VXH.js.map +1 -0
- package/dist/geo-world-35HL56UU.js +8 -0
- package/dist/geo-world-35HL56UU.js.map +1 -0
- package/dist/slide/index.d.ts +2 -2
- package/dist/slide/index.js +3 -3
- package/dist/table/index.js +4 -5
- package/dist/table/index.js.map +1 -1
- package/dist/ui/index.js +2 -3
- package/dist/ui/index.js.map +1 -1
- package/dist/{upgrade-6DT7ADRP.js → upgrade-MCHMELWC.js} +3 -3
- package/dist/{upgrade-6DT7ADRP.js.map → upgrade-MCHMELWC.js.map} +1 -1
- package/docs/en/agent-skills.md +13 -14
- package/docs/en/changelog.md +8 -0
- package/docs/en/parts/draft-chart.md +42 -0
- package/docs/en/parts/page-shell.md +1 -1
- package/docs/index.json +70 -50
- package/docs/ja/agent-skills.md +14 -14
- package/docs/ja/changelog.md +8 -0
- package/docs/ja/parts/draft-chart.md +40 -0
- package/docs/ja/parts/page-shell.md +1 -1
- package/package.json +2 -2
- package/registry/parts.json +5221 -0
- package/registry/ui/chart-frame.tsx +8 -0
- package/registry/ui/combo-chart.tsx +1852 -0
- package/registry/ui/dashboard-card.tsx +3 -1
- package/registry/ui/data-table.tsx +3 -1
- package/registry/ui/delta.tsx +3 -1
- package/registry/ui/filter-bar.tsx +3 -1
- package/registry/ui/forecast-chart.tsx +4734 -0
- package/registry/ui/funnel-steps.tsx +1 -1
- package/registry/ui/kpi-card.tsx +3 -1
- package/registry/ui/metric-value.tsx +2 -2
- package/registry/ui/page-shell.tsx +25 -4
- package/registry/ui/paired-chart.tsx +1 -1
- package/registry/ui/pivot-table.tsx +1260 -0
- package/registry/ui/progress-circle.tsx +2 -2
- package/registry/ui/sparkline.tsx +1 -1
- package/registry/ui/treemap.tsx +1917 -0
- package/templates/AGENTS.md +1 -1
- package/dist/add-4GHF2TX2.js +0 -6
- package/dist/add-4GHF2TX2.js.map +0 -1
- package/dist/chunk-2IAPIOPS.js.map +0 -1
- package/dist/chunk-3RJEOP3J.js.map +0 -1
- package/dist/chunk-77CISBMG.js.map +0 -1
- package/dist/chunk-AABMKHEU.js.map +0 -1
- package/dist/chunk-NZ5QIIG4.js +0 -74
- package/dist/chunk-NZ5QIIG4.js.map +0 -1
- package/dist/chunk-QNOX7WLZ.js.map +0 -1
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { ChartFrameContext, useChartSurface, resolveChartColors,
|
|
2
|
-
import { Sparkline } from './chunk-
|
|
3
|
-
import { formatDateRange, EMPTY_VALUE,
|
|
4
|
-
import { Tooltip, TooltipTrigger, TooltipContent, Badge, SidebarProvider, Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarFooter, SidebarRail, SidebarInset, SidebarTrigger, Separator, Input, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, useIsMobile, Popover, PopoverTrigger, PopoverContent, Calendar, ToggleGroup, ToggleGroupItem, SelectGroup, SelectLabel, Collapsible, CollapsibleTrigger, SidebarMenuButton, SidebarMenuBadge, CollapsibleContent, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton, SidebarMenuItem, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Checkbox, Card, CardHeader, CardDescription, CardTitle, CardAction, CardFooter } from './chunk-
|
|
1
|
+
import { ChartFrameContext, useChartSurface, resolveChartColors, resolveFillColors, EChart, CHART_OTHER_COLOR } from './chunk-EG4PX726.js';
|
|
2
|
+
import { Sparkline } from './chunk-P2KIHF65.js';
|
|
3
|
+
import { formatDateRange, EMPTY_VALUE, toValueFormatter, formatDate, formatValue, formatPoints, formatKind, formatDelta, MINUS_SIGN, DEFAULT_LOCALE, formatDatesInText } from './chunk-XRKSIC5Z.js';
|
|
4
|
+
import { Tooltip, TooltipTrigger, TooltipContent, Badge, SidebarProvider, Sidebar, SidebarContent, SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarFooter, SidebarRail, SidebarInset, SidebarTrigger, Separator, Input, Table, TableHeader, TableRow, TableHead, TableBody, TableCell, Skeleton, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, useIsMobile, Popover, PopoverTrigger, PopoverContent, Calendar, ToggleGroup, ToggleGroupItem, SelectGroup, SelectLabel, Collapsible, CollapsibleTrigger, SidebarMenuButton, SidebarMenuBadge, CollapsibleContent, SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton, SidebarMenuItem, Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, Empty, Checkbox, Card, CardHeader, CardDescription, CardTitle, CardAction, CardFooter } from './chunk-MQUIAP66.js';
|
|
5
5
|
import { useLocale, uiLanguage, Button, weekStartsOn, resolveLocale, defaultFiscalYearStartMonth, FALLBACK_LOCALE } from './chunk-ROHNXP7J.js';
|
|
6
6
|
import { cn } from './chunk-U736PVHY.js';
|
|
7
|
-
import { forwardRef, useState, useEffect, createContext, useId, useMemo, useRef, useCallback, Fragment as Fragment$1, useContext } from 'react';
|
|
7
|
+
import { forwardRef, useState, useEffect, createContext, useId, useMemo, useRef, useCallback, Fragment as Fragment$1, useContext, useInsertionEffect, useLayoutEffect } from 'react';
|
|
8
8
|
import { cva } from 'class-variance-authority';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import { Search, Columns3, CalendarIcon, ChevronDownIcon, ArrowUp, ArrowDown, ArrowRight, Minus, ChevronRight, ArrowUpDown, XIcon, LoaderIcon, CheckIcon, Download, ChevronDown, X, CircleX, TriangleAlert, CircleCheck } from 'lucide-react';
|
|
11
|
-
import { use } from 'echarts/core';
|
|
12
|
-
import { GraphicComponent, VisualMapComponent } from 'echarts/components';
|
|
13
|
-
import { ScatterChart as ScatterChart$1, HeatmapChart } from 'echarts/charts';
|
|
11
|
+
import { use, registerMap } from 'echarts/core';
|
|
12
|
+
import { GraphicComponent, VisualMapComponent, GeoComponent } from 'echarts/components';
|
|
13
|
+
import { ScatterChart as ScatterChart$1, HeatmapChart, MapChart, TreemapChart } from 'echarts/charts';
|
|
14
14
|
import 'echarts';
|
|
15
|
+
import { subMonths, subDays, endOfDay, startOfDay, startOfWeek, subWeeks, endOfWeek, startOfMonth, endOfMonth, isSameDay, addMonths, differenceInCalendarDays, parseISO } from 'date-fns';
|
|
15
16
|
import { flexRender, useReactTable, getPaginationRowModel, getFilteredRowModel, getSortedRowModel, getCoreRowModel } from '@tanstack/react-table';
|
|
16
|
-
import { subMonths, subDays, endOfDay, startOfDay, startOfWeek, subWeeks, endOfWeek, startOfMonth, endOfMonth, isSameDay, addMonths } from 'date-fns';
|
|
17
17
|
import { ja } from 'date-fns/locale';
|
|
18
18
|
|
|
19
19
|
// @squadbase/vantage — generated build. Do not edit.
|
|
@@ -127,7 +127,13 @@ var UpdatedAt = forwardRef(
|
|
|
127
127
|
);
|
|
128
128
|
UpdatedAt.displayName = "UpdatedAt";
|
|
129
129
|
var PageShell = forwardRef(
|
|
130
|
-
({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, "data-slot": "page-shell", className: cn("flex flex-col", className), ...props
|
|
130
|
+
({ className, title, description, children, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, "data-slot": "page-shell", className: cn("flex flex-col", className), ...props, children: title === void 0 ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
131
|
+
/* @__PURE__ */ jsx(PageShellHeader, { children: /* @__PURE__ */ jsxs(PageShellHeading, { children: [
|
|
132
|
+
/* @__PURE__ */ jsx(PageShellTitle, { children: title }),
|
|
133
|
+
description ? /* @__PURE__ */ jsx(PageShellDescription, { children: description }) : null
|
|
134
|
+
] }) }),
|
|
135
|
+
/* @__PURE__ */ jsx(PageShellContent, { children })
|
|
136
|
+
] }) })
|
|
131
137
|
);
|
|
132
138
|
PageShell.displayName = "PageShell";
|
|
133
139
|
var SOURCE_PREFIX = { ja: "\u51FA\u5178\uFF1A", en: "Source: " };
|
|
@@ -690,6 +696,7 @@ var ChartFrame = forwardRef(function ChartFrame2({
|
|
|
690
696
|
ariaLabel,
|
|
691
697
|
plotRole = "img",
|
|
692
698
|
dataTable,
|
|
699
|
+
footnote,
|
|
693
700
|
className,
|
|
694
701
|
children,
|
|
695
702
|
...props
|
|
@@ -720,6 +727,7 @@ var ChartFrame = forwardRef(function ChartFrame2({
|
|
|
720
727
|
children: state === "loading" ? /* @__PURE__ */ jsx(ChartSkeleton, { variant, loadingText: t.loading }) : state === "empty" ? /* @__PURE__ */ jsx(ChartEmpty, { ...emptyState, defaultTitle: t.emptyTitle }) : /* @__PURE__ */ jsx(ChartFrameContext.Provider, { value: true, children })
|
|
721
728
|
}
|
|
722
729
|
),
|
|
730
|
+
footnote && /* @__PURE__ */ jsx("p", { "data-slot": "chart-frame-footnote", className: "text-xs leading-snug text-muted-foreground", children: footnote }),
|
|
723
731
|
state === "ready" && dataTable && dataTable.rows.length > 0 && /* @__PURE__ */ jsx(ChartDataTableView, { table: dataTable, fallbackCaption: title ?? ariaLabel })
|
|
724
732
|
] });
|
|
725
733
|
});
|
|
@@ -743,7 +751,7 @@ function ChartDataTableView({ table, fallbackCaption }) {
|
|
|
743
751
|
caption && /* @__PURE__ */ jsx("caption", { children: caption }),
|
|
744
752
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: table.columns.map((column, i) => /* @__PURE__ */ jsx("th", { scope: "col", children: column }, i)) }) }),
|
|
745
753
|
/* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: row.map(
|
|
746
|
-
(
|
|
754
|
+
(cell2, c) => c === 0 ? /* @__PURE__ */ jsx("th", { scope: "row", children: cell2 }, c) : /* @__PURE__ */ jsx("td", { children: cell2 }, c)
|
|
747
755
|
) }, r)) })
|
|
748
756
|
] }) });
|
|
749
757
|
}
|
|
@@ -800,8 +808,8 @@ function chartColorAt(colors, i) {
|
|
|
800
808
|
const palette = colors && colors.length > 0 ? colors : DEFAULT_CHART_COLORS;
|
|
801
809
|
return palette[i % palette.length];
|
|
802
810
|
}
|
|
803
|
-
function tintCss(cssColor,
|
|
804
|
-
return `color-mix(in oklab, ${cssColor} ${
|
|
811
|
+
function tintCss(cssColor, percent2) {
|
|
812
|
+
return `color-mix(in oklab, ${cssColor} ${percent2}%, transparent)`;
|
|
805
813
|
}
|
|
806
814
|
function resolveValueFormatter(valueFormatter, format, locale) {
|
|
807
815
|
return valueFormatter ?? toValueFormatter(format, locale);
|
|
@@ -1217,20 +1225,20 @@ function baselineOfLabel(named, language) {
|
|
|
1217
1225
|
}
|
|
1218
1226
|
return named;
|
|
1219
1227
|
}
|
|
1220
|
-
function comparisonBaseline(vs, grain, label2,
|
|
1228
|
+
function comparisonBaseline(vs, grain, label2, toDate3, locale) {
|
|
1221
1229
|
const language = uiLanguage(resolveLocale(locale));
|
|
1222
1230
|
const nouns = BASELINE_NOUNS[language];
|
|
1223
1231
|
const named = label2?.trim();
|
|
1224
1232
|
if (named) return baselineOfLabel(named, language);
|
|
1225
|
-
if (vs === "custom") return nouns.custom;
|
|
1226
1233
|
if (vs === "target") return nouns.target;
|
|
1227
|
-
if (vs === "prior-year" &&
|
|
1234
|
+
if (vs === "prior-year" && toDate3) return nouns["prior-year"].unknown;
|
|
1235
|
+
if (vs !== "prior-year" && vs !== "prior-period") return nouns.custom;
|
|
1228
1236
|
return nouns[vs][grain ?? "unknown"];
|
|
1229
1237
|
}
|
|
1230
|
-
function comparisonLabel(vs, grain, label2,
|
|
1238
|
+
function comparisonLabel(vs, grain, label2, toDate3, locale) {
|
|
1231
1239
|
if (label2?.trim()) return label2;
|
|
1232
1240
|
const resolved = resolveLocale(locale);
|
|
1233
|
-
return LABEL_OF[uiLanguage(resolved)](comparisonBaseline(vs, grain, void 0,
|
|
1241
|
+
return LABEL_OF[uiLanguage(resolved)](comparisonBaseline(vs, grain, void 0, toDate3, resolved));
|
|
1234
1242
|
}
|
|
1235
1243
|
var toNumber = (v) => v === null || v === void 0 || Number.isNaN(v) ? null : v;
|
|
1236
1244
|
function computeComparison(current, comparison, options) {
|
|
@@ -1740,9 +1748,9 @@ function buildCartesianOption(cfg) {
|
|
|
1740
1748
|
const text = chartText(locale);
|
|
1741
1749
|
const provisionalLabel = cfg.provisionalLabel ?? text.provisional;
|
|
1742
1750
|
const stacked = kind !== "line" && type !== "default";
|
|
1743
|
-
const
|
|
1751
|
+
const percent2 = kind !== "line" && type === "percent";
|
|
1744
1752
|
const horizontalBars = kind === "bar" && layout === "vertical";
|
|
1745
|
-
const comparison = kind !== "bar" && !
|
|
1753
|
+
const comparison = kind !== "bar" && !percent2 ? cfg.comparison : void 0;
|
|
1746
1754
|
const compIdx = comparison ? categories.indexOf(comparison.category) : -1;
|
|
1747
1755
|
const mainIdx = categories.map((_, i) => i).filter((i) => i !== compIdx);
|
|
1748
1756
|
const seriesType = kind === "line" ? "default" : type;
|
|
@@ -1757,11 +1765,11 @@ function buildCartesianOption(cfg) {
|
|
|
1757
1765
|
const rawIndex = data.map((row) => row[index]);
|
|
1758
1766
|
const lastActual = labels.length - 1;
|
|
1759
1767
|
const provStart = categories.length > 0 ? provisionalStart(rawIndex, cfg.provisionalFrom) : -1;
|
|
1760
|
-
const
|
|
1768
|
+
const forecast2 = kind !== "bar" && !percent2 && cfg.forecast && cfg.forecast.data.length > 0 && data.length > 0 && categories.length > 0 ? cfg.forecast : void 0;
|
|
1761
1769
|
const forecastAt = [];
|
|
1762
|
-
if (
|
|
1770
|
+
if (forecast2) {
|
|
1763
1771
|
const position = new Map(labels.map((label2, i) => [label2, i]));
|
|
1764
|
-
for (const row of
|
|
1772
|
+
for (const row of forecast2.data) {
|
|
1765
1773
|
const label2 = String(row[index] ?? "");
|
|
1766
1774
|
let at = position.get(label2);
|
|
1767
1775
|
if (at === void 0 || at <= lastActual) {
|
|
@@ -1798,14 +1806,14 @@ function buildCartesianOption(cfg) {
|
|
|
1798
1806
|
(title, i) => nameLabels ? hasLabel(nameLabels, labels[i] ?? "") ? labelOf(labels[i] ?? "", nameLabels) : title : title
|
|
1799
1807
|
);
|
|
1800
1808
|
const highlight = cfg.highlight ?? defaultChartHighlight(kind, data, index);
|
|
1801
|
-
const stackTotals = stacked && !
|
|
1809
|
+
const stackTotals = stacked && !percent2 ? stackTotalsOf(mainIdx.map((i) => values[i])) : null;
|
|
1802
1810
|
const highlightSeries = (stackTotals ? mainIdx[mainIdx.length - 1] : mainIdx[0]) ?? 0;
|
|
1803
|
-
const shareHighlight =
|
|
1804
|
-
const picked = highlight !== "auto" || mainIdx.length === 0 ||
|
|
1811
|
+
const shareHighlight = percent2 && kind === "area";
|
|
1812
|
+
const picked = highlight !== "auto" || mainIdx.length === 0 || percent2 && !shareHighlight ? [] : shareHighlight ? categories.length <= 2 ? pickShareHighlightPoints(values[0] ?? []) : [] : stackTotals ? pickHighlightPoints(stackTotals) : mainIdx.length <= 2 ? pickHighlightPoints(values[highlightSeries] ?? []) : [];
|
|
1805
1813
|
const pointGap = plotWidth && labels.length > 1 ? plotWidth / (kind === "bar" ? labels.length : labels.length - 1) : null;
|
|
1806
1814
|
const latestOnly = cfg.highlight === void 0 && !shareHighlight && picked.length === 2;
|
|
1807
1815
|
const highlightPoints = latestOnly ? [picked[1]] : picked.length === 2 && pointGap !== null && Math.abs(picked[1].index - picked[0].index) * pointGap < HIGHLIGHT_LABEL_WIDTH ? [picked[1]] : picked;
|
|
1808
|
-
const boundaryIndex = kind !== "bar" && !
|
|
1816
|
+
const boundaryIndex = kind !== "bar" && !percent2 && cfg.forecast && cfg.forecast.data.length > 0 ? lastActual : -1;
|
|
1809
1817
|
const edgeAlign = (i) => horizontalBars || labels.length < 2 ? void 0 : i === boundaryIndex && i !== last ? { align: "right", offset: [-6, 0] } : i === last ? { align: "right", ...kind === "bar" ? { offset: [8, 0] } : {} } : i === 0 ? { align: "left", ...kind === "bar" ? { offset: [-8, 0] } : {} } : void 0;
|
|
1810
1818
|
const perBarTarget = kind === "bar" && Boolean(targetKey);
|
|
1811
1819
|
const hasTarget = !perBarTarget && target !== void 0 && Number.isFinite(target);
|
|
@@ -1826,7 +1834,7 @@ function buildCartesianOption(cfg) {
|
|
|
1826
1834
|
const hideTargetLabel = kind === "line" && targetLabelPosition === "legend" || !shareHighlight && plotWidth !== void 0 && plotWidth < TARGET_LABEL_NARROW_WIDTH && highlightPoints.some((p) => p.index === labels.length - 1 && (nearTargetLabel(p, "under") || nearTargetLabel(p, "over")));
|
|
1827
1835
|
const below = (p) => !shareHighlight && !hideTargetLabel && nearTargetLabel(p, "under");
|
|
1828
1836
|
const targetLabelBelow = !shareHighlight && !hideTargetLabel && highlightPoints.some((p) => nearTargetLabel(p, "over"));
|
|
1829
|
-
const barTargets = perBarTarget && targetKey && !
|
|
1837
|
+
const barTargets = perBarTarget && targetKey && !percent2 && (stacked || categories.length === 1) ? barTargetValues(data, targetKey) : null;
|
|
1830
1838
|
const barTargetName = targetLabel ?? text.target;
|
|
1831
1839
|
const barTargetColor = labelColor ?? guideColor;
|
|
1832
1840
|
const markPointFor = (c, color) => c === highlightSeries && highlightPoints.length > 0 ? {
|
|
@@ -1951,7 +1959,7 @@ function buildCartesianOption(cfg) {
|
|
|
1951
1959
|
symbolSize: 6,
|
|
1952
1960
|
showSymbol: showDots || data.length === 1,
|
|
1953
1961
|
// 100% stacked: the top band's upper edge is the 100% line, not a value — drawn, it reads as「this band = 100%」.
|
|
1954
|
-
lineStyle:
|
|
1962
|
+
lineStyle: percent2 && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, color, opacity: dimSeries ? DIMMED : 1 },
|
|
1955
1963
|
itemStyle: { color, opacity: dimSeries ? DIMMED : 1 },
|
|
1956
1964
|
// Hovering a series fades the others (not for a stack, where the bands read together).
|
|
1957
1965
|
emphasis: stacked ? void 0 : { focus: "series" },
|
|
@@ -1980,7 +1988,7 @@ function buildCartesianOption(cfg) {
|
|
|
1980
1988
|
symbol: "circle",
|
|
1981
1989
|
symbolSize: 6,
|
|
1982
1990
|
showSymbol: showDots || data.length === 1,
|
|
1983
|
-
lineStyle:
|
|
1991
|
+
lineStyle: percent2 && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, type: "dashed", color, opacity },
|
|
1984
1992
|
itemStyle: { color, opacity },
|
|
1985
1993
|
areaStyle: solid.areaStyle ? { ...solid.areaStyle, opacity: (solid.areaStyle.opacity ?? 1) * PROVISIONAL_OPACITY } : void 0,
|
|
1986
1994
|
markPoint: void 0,
|
|
@@ -2006,11 +2014,11 @@ function buildCartesianOption(cfg) {
|
|
|
2006
2014
|
data: []
|
|
2007
2015
|
});
|
|
2008
2016
|
}
|
|
2009
|
-
const forecastLabel =
|
|
2017
|
+
const forecastLabel = forecast2?.label ?? text.forecast;
|
|
2010
2018
|
const forecastBandAlpha = isDarkSurface(labelBackground) ? FORECAST_BAND_ALPHA_DARK : FORECAST_BAND_ALPHA_LIGHT;
|
|
2011
2019
|
const forecastValues = [];
|
|
2012
|
-
if (
|
|
2013
|
-
const category =
|
|
2020
|
+
if (forecast2) {
|
|
2021
|
+
const category = forecast2.category !== void 0 && categories.includes(forecast2.category) && forecast2.category !== comparison?.category ? forecast2.category : categories[mainIdx[0] ?? 0];
|
|
2014
2022
|
const c = categories.indexOf(category);
|
|
2015
2023
|
const color = palette[c] || void 0;
|
|
2016
2024
|
const actual = values[c];
|
|
@@ -2030,7 +2038,7 @@ function buildCartesianOption(cfg) {
|
|
|
2030
2038
|
range[start] = 0;
|
|
2031
2039
|
}
|
|
2032
2040
|
let hasBand = false;
|
|
2033
|
-
|
|
2041
|
+
forecast2.data.forEach((row, k) => {
|
|
2034
2042
|
const at = forecastAt[k];
|
|
2035
2043
|
line[at] = cellNumber(row.value);
|
|
2036
2044
|
const lo = cellNumber(row.lower);
|
|
@@ -2096,7 +2104,7 @@ function buildCartesianOption(cfg) {
|
|
|
2096
2104
|
const totals = labels.map(
|
|
2097
2105
|
(_, i) => stacked ? values.reduce((sum, v) => sum + Math.max(0, v[i] ?? 0), 0) : Math.max(0, ...values.map((v) => v[i] ?? 0))
|
|
2098
2106
|
);
|
|
2099
|
-
const top =
|
|
2107
|
+
const top = percent2 ? 100 : maxValue ?? Math.max(target, ...totals);
|
|
2100
2108
|
if (!(top > 0)) return "center";
|
|
2101
2109
|
const ratio = Math.min(1, Math.max(0, target / top));
|
|
2102
2110
|
const half = (estimateTextWidth(targetText) + 8) / 2;
|
|
@@ -2129,7 +2137,7 @@ function buildCartesianOption(cfg) {
|
|
|
2129
2137
|
}
|
|
2130
2138
|
});
|
|
2131
2139
|
}
|
|
2132
|
-
if (
|
|
2140
|
+
if (forecast2) {
|
|
2133
2141
|
const forecastSpan = plotWidth && labels.length > 1 ? Math.max(1, plotWidth - (showYAxis ? yAxisWidth ?? Y_AXIS_ESTIMATE : 0) - LINE_RIGHT_MARGIN) / (labels.length - 1) * (labels.length - 1 - lastActual) + LINE_RIGHT_MARGIN : null;
|
|
2134
2142
|
markLines.push({
|
|
2135
2143
|
name: "forecast-boundary",
|
|
@@ -2280,16 +2288,16 @@ function buildCartesianOption(cfg) {
|
|
|
2280
2288
|
const fit = kind === "line" && (fitToData ?? autoMinValue);
|
|
2281
2289
|
const valueSplitNumber = (() => {
|
|
2282
2290
|
if (!horizontalBars || !plotWidth) return void 0;
|
|
2283
|
-
const top =
|
|
2291
|
+
const top = percent2 ? 100 : maxValue ?? Math.max(0, ...(stacked ? stackTotalsOf(values) : values.flat()).map((v) => v ?? 0));
|
|
2284
2292
|
const categoryColumn = showYAxis ? (yAxisWidth ?? Math.min(160, Math.max(0, ...labels.map(estimateTextWidth)))) + 8 : 0;
|
|
2285
2293
|
const plot = plotWidth - categoryColumn - (highlightPoints.length > 0 ? 64 : 16) - 4;
|
|
2286
|
-
const text2 =
|
|
2294
|
+
const text2 = percent2 ? (v) => `${v}%` : (v) => axisFormatter(v);
|
|
2287
2295
|
return valueAxisSplitNumber(plot, [text2(0), text2(top)]);
|
|
2288
2296
|
})();
|
|
2289
2297
|
const limits = valueAxisLimits({ minValue, maxValue }, kind !== "line");
|
|
2290
2298
|
const splitNumber = horizontalBars ? valueSplitNumber : valueAxisSplitCount(plotHeight);
|
|
2291
2299
|
const tickFormatter = (() => {
|
|
2292
|
-
if (!cfg.axisTicks ||
|
|
2300
|
+
if (!cfg.axisTicks || percent2) return axisFormatter;
|
|
2293
2301
|
const extentValues = [];
|
|
2294
2302
|
const addAll = (list) => {
|
|
2295
2303
|
for (const v of list) if (v !== null && Number.isFinite(v)) extentValues.push(v);
|
|
@@ -2297,7 +2305,7 @@ function buildCartesianOption(cfg) {
|
|
|
2297
2305
|
if (stackTotals) addAll(stackTotals);
|
|
2298
2306
|
else values.forEach(addAll);
|
|
2299
2307
|
addAll(forecastValues);
|
|
2300
|
-
if (
|
|
2308
|
+
if (forecast2) addAll(forecast2.data.flatMap((row) => [cellNumber(row.lower), cellNumber(row.upper)]));
|
|
2301
2309
|
if (barTargets) addAll(barTargets);
|
|
2302
2310
|
if (hasTarget && target !== void 0) extentValues.push(target);
|
|
2303
2311
|
if (maxValue !== void 0 && Number.isFinite(maxValue)) extentValues.push(maxValue);
|
|
@@ -2314,8 +2322,8 @@ function buildCartesianOption(cfg) {
|
|
|
2314
2322
|
show: horizontalBars ? showXAxis : showYAxis,
|
|
2315
2323
|
// Bars and areas always start at 0 (a positive `minValue` can't lift them, a negative one makes room below);
|
|
2316
2324
|
// lines do too unless asked to fit the data. A limit never cuts the data: the axis widens to it.
|
|
2317
|
-
min:
|
|
2318
|
-
max:
|
|
2325
|
+
min: percent2 ? 0 : kind === "line" ? minValue === void 0 ? fit ? void 0 : zeroBasedMin : limits.min : limits.min,
|
|
2326
|
+
max: percent2 ? 100 : limits.max,
|
|
2319
2327
|
scale: fit && minValue == null,
|
|
2320
2328
|
splitNumber,
|
|
2321
2329
|
minInterval: allowDecimals ? void 0 : 1,
|
|
@@ -2323,7 +2331,7 @@ function buildCartesianOption(cfg) {
|
|
|
2323
2331
|
axisTick: { show: false },
|
|
2324
2332
|
splitLine: { show: showGridLines },
|
|
2325
2333
|
axisLabel: {
|
|
2326
|
-
formatter:
|
|
2334
|
+
formatter: percent2 ? (v) => `${v}%` : (v) => tickFormatter(v),
|
|
2327
2335
|
// Fitted to the data: the lowest tick would print over the first x label at the bottom left. The gridline stays.
|
|
2328
2336
|
showMinLabel: fit && minValue == null && !horizontalBars ? false : void 0,
|
|
2329
2337
|
// Horizontal: ticks that would still touch on a narrow plot are dropped.
|
|
@@ -2337,7 +2345,7 @@ function buildCartesianOption(cfg) {
|
|
|
2337
2345
|
};
|
|
2338
2346
|
const legendItems = [
|
|
2339
2347
|
...categories,
|
|
2340
|
-
...
|
|
2348
|
+
...forecast2 ? [forecastLabel] : [],
|
|
2341
2349
|
...provStart !== -1 ? [provisionalLabel] : [],
|
|
2342
2350
|
...barTargets ? [barTargetName] : [],
|
|
2343
2351
|
...hasTarget ? [targetText] : []
|
|
@@ -2358,7 +2366,7 @@ function buildCartesianOption(cfg) {
|
|
|
2358
2366
|
// The target marker is a tick, not a dot.
|
|
2359
2367
|
// The target is a row of dots and the forecast a row of dashes — their lines' patterns — and a per-bar target a tick.
|
|
2360
2368
|
data: legendItems.map(
|
|
2361
|
-
(name) => barTargets && name === barTargetName ? { name, icon: TARGET_LEGEND_ICON } :
|
|
2369
|
+
(name) => barTargets && name === barTargetName ? { name, icon: TARGET_LEGEND_ICON } : forecast2 && name === forecastLabel ? { name, icon: FORECAST_DASHED_ICON } : hasTarget && name === targetText ? { name, icon: TARGET_DOTTED_ICON } : name
|
|
2362
2370
|
),
|
|
2363
2371
|
formatter: nameLabels ? (name) => labelOf(name, nameLabels) : void 0
|
|
2364
2372
|
},
|
|
@@ -2366,7 +2374,7 @@ function buildCartesianOption(cfg) {
|
|
|
2366
2374
|
top: (showLegend ? 12 + LEGEND_LINE_HEIGHT * legendLines(legendItems, plotWidth, reserveActions ? ACTIONS_RESERVE : 0) : 12) + // The flat target label of a horizontal bar chart sits above the plot.
|
|
2367
2375
|
(horizontalBars && hasTarget ? 18 : 0) + // The label of a band over bars sits above the plot's top edge.
|
|
2368
2376
|
(kind === "bar" && notes.some((n) => n.to !== void 0) ? BAND_LABEL_ROOM : 0) + // The boundary label of a forecast sits above the plot's top edge too.
|
|
2369
|
-
(
|
|
2377
|
+
(forecast2 ? FORECAST_LABEL_ROOM : 0),
|
|
2370
2378
|
// Lines start and end on the outermost ticks, so the last x label (and an edge
|
|
2371
2379
|
// highlight) hangs past the plot; a horizontal bar's highlight sits past its end.
|
|
2372
2380
|
right: horizontalBars ? highlightPoints.length > 0 ? 64 : 16 : kind === "bar" ? 16 : LINE_RIGHT_MARGIN,
|
|
@@ -2397,7 +2405,7 @@ function buildCartesianOption(cfg) {
|
|
|
2397
2405
|
)
|
|
2398
2406
|
};
|
|
2399
2407
|
};
|
|
2400
|
-
const shares =
|
|
2408
|
+
const shares = percent2 ? cartesianSeriesValues(i <= lastActual ? [row] : [], categories, "percent") : null;
|
|
2401
2409
|
const legendRank = (p) => {
|
|
2402
2410
|
const at = categories.indexOf(p.seriesName ?? "");
|
|
2403
2411
|
return at === -1 ? categories.length : at;
|
|
@@ -2642,12 +2650,12 @@ function AreaChartImpl({
|
|
|
2642
2650
|
provisionalFrom,
|
|
2643
2651
|
provisionalLabel,
|
|
2644
2652
|
isLoading,
|
|
2645
|
-
loading,
|
|
2653
|
+
loading: loading2,
|
|
2646
2654
|
emptyState,
|
|
2647
2655
|
height,
|
|
2648
2656
|
chartOptions,
|
|
2649
2657
|
areaOptions,
|
|
2650
|
-
forecast,
|
|
2658
|
+
forecast: forecast2,
|
|
2651
2659
|
comparison,
|
|
2652
2660
|
highlight,
|
|
2653
2661
|
target,
|
|
@@ -2729,7 +2737,7 @@ function AreaChartImpl({
|
|
|
2729
2737
|
target,
|
|
2730
2738
|
targetLabel,
|
|
2731
2739
|
annotations,
|
|
2732
|
-
forecast,
|
|
2740
|
+
forecast: forecast2,
|
|
2733
2741
|
comparison: comparisonConfig,
|
|
2734
2742
|
comparisonFormat: format,
|
|
2735
2743
|
provisionalFrom,
|
|
@@ -2775,7 +2783,7 @@ function AreaChartImpl({
|
|
|
2775
2783
|
target,
|
|
2776
2784
|
targetLabel,
|
|
2777
2785
|
annotations,
|
|
2778
|
-
|
|
2786
|
+
forecast2,
|
|
2779
2787
|
comparisonConfig,
|
|
2780
2788
|
format,
|
|
2781
2789
|
height,
|
|
@@ -2803,7 +2811,7 @@ function AreaChartImpl({
|
|
|
2803
2811
|
ref: frameRef,
|
|
2804
2812
|
title,
|
|
2805
2813
|
summary: resolvedSummary,
|
|
2806
|
-
isLoading: isLoading ??
|
|
2814
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
2807
2815
|
isEmpty: data.length === 0,
|
|
2808
2816
|
emptyState,
|
|
2809
2817
|
height,
|
|
@@ -2837,7 +2845,7 @@ function BarChartImpl({
|
|
|
2837
2845
|
provisionalFrom,
|
|
2838
2846
|
provisionalLabel,
|
|
2839
2847
|
isLoading,
|
|
2840
|
-
loading,
|
|
2848
|
+
loading: loading2,
|
|
2841
2849
|
emptyState,
|
|
2842
2850
|
height,
|
|
2843
2851
|
chartOptions,
|
|
@@ -2993,7 +3001,7 @@ function BarChartImpl({
|
|
|
2993
3001
|
ref: frameRef,
|
|
2994
3002
|
title,
|
|
2995
3003
|
summary: resolvedSummary,
|
|
2996
|
-
isLoading: isLoading ??
|
|
3004
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
2997
3005
|
isEmpty: data.length === 0,
|
|
2998
3006
|
emptyState,
|
|
2999
3007
|
height,
|
|
@@ -3027,12 +3035,12 @@ function LineChartImpl({
|
|
|
3027
3035
|
provisionalFrom,
|
|
3028
3036
|
provisionalLabel,
|
|
3029
3037
|
isLoading,
|
|
3030
|
-
loading,
|
|
3038
|
+
loading: loading2,
|
|
3031
3039
|
emptyState,
|
|
3032
3040
|
height,
|
|
3033
3041
|
chartOptions,
|
|
3034
3042
|
lineOptions,
|
|
3035
|
-
forecast,
|
|
3043
|
+
forecast: forecast2,
|
|
3036
3044
|
comparison,
|
|
3037
3045
|
highlight,
|
|
3038
3046
|
target,
|
|
@@ -3115,7 +3123,7 @@ function LineChartImpl({
|
|
|
3115
3123
|
targetLabel,
|
|
3116
3124
|
targetLabelPosition,
|
|
3117
3125
|
annotations,
|
|
3118
|
-
forecast,
|
|
3126
|
+
forecast: forecast2,
|
|
3119
3127
|
comparison: comparisonConfig,
|
|
3120
3128
|
comparisonFormat: format,
|
|
3121
3129
|
provisionalFrom,
|
|
@@ -3162,7 +3170,7 @@ function LineChartImpl({
|
|
|
3162
3170
|
targetLabel,
|
|
3163
3171
|
targetLabelPosition,
|
|
3164
3172
|
annotations,
|
|
3165
|
-
|
|
3173
|
+
forecast2,
|
|
3166
3174
|
comparisonConfig,
|
|
3167
3175
|
format,
|
|
3168
3176
|
height,
|
|
@@ -3190,7 +3198,7 @@ function LineChartImpl({
|
|
|
3190
3198
|
ref: frameRef,
|
|
3191
3199
|
title,
|
|
3192
3200
|
summary: resolvedSummary,
|
|
3193
|
-
isLoading: isLoading ??
|
|
3201
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
3194
3202
|
isEmpty: data.length === 0,
|
|
3195
3203
|
emptyState,
|
|
3196
3204
|
height,
|
|
@@ -3234,7 +3242,7 @@ function DonutChartImpl({
|
|
|
3234
3242
|
actions,
|
|
3235
3243
|
fileName,
|
|
3236
3244
|
isLoading,
|
|
3237
|
-
loading,
|
|
3245
|
+
loading: loading2,
|
|
3238
3246
|
emptyState,
|
|
3239
3247
|
height,
|
|
3240
3248
|
chartOptions,
|
|
@@ -3328,7 +3336,7 @@ function DonutChartImpl({
|
|
|
3328
3336
|
ref: frameRef,
|
|
3329
3337
|
title,
|
|
3330
3338
|
summary: resolvedSummary,
|
|
3331
|
-
isLoading: isLoading ??
|
|
3339
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
3332
3340
|
isEmpty: data.length === 0,
|
|
3333
3341
|
emptyState,
|
|
3334
3342
|
height,
|
|
@@ -5083,10 +5091,10 @@ var HEATMAP_CELL_PADDING = 4;
|
|
|
5083
5091
|
var HEATMAP_BUCKET_MID = 20;
|
|
5084
5092
|
function heatmapCellFontSize(plotWidth, columns, rowNames, texts) {
|
|
5085
5093
|
const widest = (items) => items.reduce((m, t) => Math.max(m, estimateTextWidth2(t)), 0);
|
|
5086
|
-
const
|
|
5094
|
+
const cell2 = (plotWidth + HEATMAP_BUCKET_MID - HEATMAP_GRID_X - (widest(rowNames) + 8)) / Math.max(1, columns);
|
|
5087
5095
|
const text = widest(texts);
|
|
5088
|
-
if (text + HEATMAP_CELL_PADDING <=
|
|
5089
|
-
return text * 10 / 12 + HEATMAP_CELL_PADDING <=
|
|
5096
|
+
if (text + HEATMAP_CELL_PADDING <= cell2) return 12;
|
|
5097
|
+
return text * 10 / 12 + HEATMAP_CELL_PADDING <= cell2 ? 10 : null;
|
|
5090
5098
|
}
|
|
5091
5099
|
var TEXT13 = {
|
|
5092
5100
|
ja: {
|
|
@@ -5114,24 +5122,24 @@ function buildHeatmapOption(config) {
|
|
|
5114
5122
|
const base = config.color || "#6366f1";
|
|
5115
5123
|
const range = [mixIntoSurface(base, config.surfaceColor, HEATMAP_FLOOR_SHARE), base];
|
|
5116
5124
|
const gapColor = "rgba(0, 0, 0, 0)";
|
|
5117
|
-
const flagged = (
|
|
5118
|
-
const tooltip = (
|
|
5119
|
-
const head = `${escapeHtml2(label2(model.ys[
|
|
5120
|
-
const main =
|
|
5125
|
+
const flagged = (cell2) => cell2.flagged ? { borderColor: config.flagColor, borderWidth: 3, borderType: "solid" } : { borderColor: "transparent", borderWidth: 1 };
|
|
5126
|
+
const tooltip = (cell2) => {
|
|
5127
|
+
const head = `${escapeHtml2(label2(model.ys[cell2.yi]))} ${escapeHtml2(label2(model.xs[cell2.xi]))}`;
|
|
5128
|
+
const main = cell2.value === null ? EMPTY_VALUE : escapeHtml2(valueFormatter(cell2.value));
|
|
5121
5129
|
const extras = (config.details ?? []).map((key) => {
|
|
5122
|
-
const n =
|
|
5130
|
+
const n = cell2.row ? toChartNumber(cell2.row[key]) : null;
|
|
5123
5131
|
return `${escapeHtml2(label2(key))} ${n === null ? EMPTY_VALUE : escapeHtml2(valueFormatter(n))}`;
|
|
5124
5132
|
}).join(t.join);
|
|
5125
|
-
const flag =
|
|
5133
|
+
const flag = cell2.flagged ? `${t.join}${t.flagged}` : "";
|
|
5126
5134
|
return `${head} ${main}${extras ? `${t.join}${extras}` : ""}${flag}`;
|
|
5127
5135
|
};
|
|
5128
5136
|
const cellFont = config.plotWidth ? heatmapCellFontSize(
|
|
5129
5137
|
config.plotWidth,
|
|
5130
5138
|
model.xs.length,
|
|
5131
5139
|
model.ys.map(label2),
|
|
5132
|
-
model.cells.flatMap((
|
|
5140
|
+
model.cells.flatMap((cell2) => cell2.value === null ? [] : [valueFormatter(cell2.value)])
|
|
5133
5141
|
) : 12;
|
|
5134
|
-
const point = (
|
|
5142
|
+
const point = (cell2) => ({ value: [cell2.xi, cell2.yi, cell2.value], cell: cell2 });
|
|
5135
5143
|
const option = {
|
|
5136
5144
|
animation: false,
|
|
5137
5145
|
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.cell) } : { show: false },
|
|
@@ -5179,11 +5187,11 @@ function buildHeatmapOption(config) {
|
|
|
5179
5187
|
{
|
|
5180
5188
|
id: "values",
|
|
5181
5189
|
type: "heatmap",
|
|
5182
|
-
data: present.map((
|
|
5183
|
-
...point(
|
|
5184
|
-
itemStyle: flagged(
|
|
5190
|
+
data: present.map((cell2) => ({
|
|
5191
|
+
...point(cell2),
|
|
5192
|
+
itemStyle: flagged(cell2),
|
|
5185
5193
|
label: {
|
|
5186
|
-
color: heatmapRatio(
|
|
5194
|
+
color: heatmapRatio(cell2.value, model.min, model.max) >= HEATMAP_LIGHT_TEXT_FROM ? config.darkCellText : config.lightCellText
|
|
5187
5195
|
}
|
|
5188
5196
|
})),
|
|
5189
5197
|
label: {
|
|
@@ -5198,7 +5206,7 @@ function buildHeatmapOption(config) {
|
|
|
5198
5206
|
{
|
|
5199
5207
|
id: "gaps",
|
|
5200
5208
|
type: "heatmap",
|
|
5201
|
-
data: missing.map((
|
|
5209
|
+
data: missing.map((cell2) => ({ value: [cell2.xi, cell2.yi, 0], cell: cell2, itemStyle: flagged(cell2) })),
|
|
5202
5210
|
label: { show: false },
|
|
5203
5211
|
itemStyle: {
|
|
5204
5212
|
color: gapColor,
|
|
@@ -5234,7 +5242,7 @@ function heatmapDataTable(model, names, formatter, labels, locale) {
|
|
|
5234
5242
|
}
|
|
5235
5243
|
function buildHeatmapSummary(model, formatter, title, labels, locale) {
|
|
5236
5244
|
let best;
|
|
5237
|
-
for (const
|
|
5245
|
+
for (const cell2 of model.cells) if (cell2.value !== null && (!best || cell2.value > best.value)) best = cell2;
|
|
5238
5246
|
if (!best) return void 0;
|
|
5239
5247
|
const label2 = (key) => labels?.[key] ?? key;
|
|
5240
5248
|
const head = title ? `${title}: ` : "";
|
|
@@ -5348,6 +5356,3064 @@ function HeatmapLegend({
|
|
|
5348
5356
|
var Heatmap = forwardRef(HeatmapImpl);
|
|
5349
5357
|
Heatmap.displayName = "Heatmap";
|
|
5350
5358
|
var TEXT14 = {
|
|
5359
|
+
ja: { item: "\u9805\u76EE", chart: (keys) => `${keys.join("\u30FB")}\u306E\u63A8\u79FB(\u4E0A\u6BB5 \u68D2\u3001\u4E0B\u6BB5 \u7DDA)` },
|
|
5360
|
+
en: { item: "Item", chart: (keys) => `Trend of ${keys.join(", ")} (bars on top, line below)` }
|
|
5361
|
+
};
|
|
5362
|
+
var NO_ROWS12 = [];
|
|
5363
|
+
var NO_KEYS = [];
|
|
5364
|
+
var PANEL_GAP = 20;
|
|
5365
|
+
var LEGEND_LINE = 24;
|
|
5366
|
+
var DEFAULT_HEIGHT = 400;
|
|
5367
|
+
var DEFAULT_Y_AXIS_WIDTH = 64;
|
|
5368
|
+
function pairedPalette(barKeys, lineKeys, barColors, lineColors) {
|
|
5369
|
+
const bars = seriesColorsFor(barKeys, barColors, CHART_OTHER_COLOR);
|
|
5370
|
+
const line = lineColors ? seriesColorsFor(lineKeys, lineColors, CHART_OTHER_COLOR) : lineKeys.map((_, i) => DEFAULT_CHART_COLORS[(barKeys.length + i) % DEFAULT_CHART_COLORS.length]);
|
|
5371
|
+
return { bars, line };
|
|
5372
|
+
}
|
|
5373
|
+
function buildPairedOption(cfg) {
|
|
5374
|
+
const { data, index, labels, barKeys, lineKeys, height, heightRatio, yAxisWidth, showLegend, showTooltip } = cfg;
|
|
5375
|
+
const legendNames = [...barKeys, ...lineKeys];
|
|
5376
|
+
const legendWidth = legendNames.reduce((sum, name) => sum + [...labels?.[name] ?? name].length * 12 + 29, 0);
|
|
5377
|
+
const budget = Math.max(80, Math.min(cfg.plotWidth ?? 720, 720) - (cfg.reserveActions ? 80 : 0));
|
|
5378
|
+
const legendRows = showLegend ? Math.min(3, Math.max(1, Math.ceil(legendWidth / budget))) : 0;
|
|
5379
|
+
const top = (showLegend ? 12 + LEGEND_LINE * legendRows : 12) + 4;
|
|
5380
|
+
const room = Math.max(120, height - top - PANEL_GAP);
|
|
5381
|
+
const [a, b] = heightRatio;
|
|
5382
|
+
const upper = Math.round(room * a / (a + b));
|
|
5383
|
+
const common = {
|
|
5384
|
+
data,
|
|
5385
|
+
index,
|
|
5386
|
+
labels,
|
|
5387
|
+
showLegend: false,
|
|
5388
|
+
showTooltip: false,
|
|
5389
|
+
highlight: "none",
|
|
5390
|
+
yAxisWidth,
|
|
5391
|
+
reducedMotion: false,
|
|
5392
|
+
locale: cfg.locale
|
|
5393
|
+
};
|
|
5394
|
+
const barOpt = buildCartesianOption({
|
|
5395
|
+
...common,
|
|
5396
|
+
kind: "bar",
|
|
5397
|
+
categories: barKeys,
|
|
5398
|
+
palette: cfg.barPalette,
|
|
5399
|
+
valueFormatter: cfg.barFormatter,
|
|
5400
|
+
axisFormatter: cfg.barAxis,
|
|
5401
|
+
axisTicks: cfg.barTicks,
|
|
5402
|
+
type: cfg.type,
|
|
5403
|
+
showXAxis: false,
|
|
5404
|
+
yAxisLabel: cfg.barAxisLabel,
|
|
5405
|
+
maxValue: cfg.barMax,
|
|
5406
|
+
plotWidth: cfg.plotWidth,
|
|
5407
|
+
plotHeight: upper
|
|
5408
|
+
});
|
|
5409
|
+
const lineOpt = buildCartesianOption({
|
|
5410
|
+
...common,
|
|
5411
|
+
kind: "line",
|
|
5412
|
+
categories: lineKeys,
|
|
5413
|
+
palette: cfg.linePalette,
|
|
5414
|
+
valueFormatter: cfg.lineFormatter,
|
|
5415
|
+
axisFormatter: cfg.lineAxis,
|
|
5416
|
+
axisTicks: cfg.lineTicks,
|
|
5417
|
+
fitToData: cfg.fitToData,
|
|
5418
|
+
showXAxis: true,
|
|
5419
|
+
yAxisLabel: cfg.lineAxisLabel,
|
|
5420
|
+
minValue: cfg.lineMin,
|
|
5421
|
+
maxValue: cfg.lineMax,
|
|
5422
|
+
guideColor: cfg.guideColor,
|
|
5423
|
+
plotWidth: cfg.plotWidth,
|
|
5424
|
+
plotHeight: room - upper
|
|
5425
|
+
});
|
|
5426
|
+
const times = timeTooltipLabels(data.map((row) => row[index]), cfg.locale);
|
|
5427
|
+
const titles = labelRows(data, index, labels).map((row, i) => String(times?.[i] ?? row[index] ?? ""));
|
|
5428
|
+
const cell2 = (row, key) => {
|
|
5429
|
+
const v = row[key];
|
|
5430
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
5431
|
+
return Number.isFinite(n) ? n : null;
|
|
5432
|
+
};
|
|
5433
|
+
const rowsOf = (row, keys, palette) => keys.map((key, i) => ({ name: labelOf(key, labels), color: palette[i] || "currentColor", value: cell2(row, key) }));
|
|
5434
|
+
const emptyTitle = tooltipHtml("", [], String, [], cfg.locale);
|
|
5435
|
+
const tooltipFormatter = (params) => {
|
|
5436
|
+
const list = Array.isArray(params) ? params : [params];
|
|
5437
|
+
const i = list[0]?.dataIndex ?? 0;
|
|
5438
|
+
const row = data[i] ?? {};
|
|
5439
|
+
const upperHtml = tooltipHtml(titles[i] ?? "", rowsOf(row, barKeys, cfg.barPalette), cfg.barFormatter, [], cfg.locale);
|
|
5440
|
+
const lowerHtml = tooltipHtml("", rowsOf(row, lineKeys, cfg.linePalette), cfg.lineFormatter, [], cfg.locale).slice(emptyTitle.length);
|
|
5441
|
+
return `${upperHtml}${lowerHtml}`;
|
|
5442
|
+
};
|
|
5443
|
+
const lineX = { ...lineOpt.xAxis, id: "x-line", gridIndex: 1, boundaryGap: true };
|
|
5444
|
+
const barX = { ...barOpt.xAxis, id: "x-bars", gridIndex: 0, show: true, axisLine: { show: false }, axisLabel: { show: false } };
|
|
5445
|
+
const gridLeft = barOpt.grid.left ?? 4;
|
|
5446
|
+
const right = 16;
|
|
5447
|
+
const option = {
|
|
5448
|
+
...barOpt,
|
|
5449
|
+
legend: { ...barOpt.legend, show: showLegend, data: legendNames, right: cfg.reserveActions ? 80 : 0 },
|
|
5450
|
+
grid: [
|
|
5451
|
+
{ top, height: upper, left: gridLeft, right, containLabel: true },
|
|
5452
|
+
{ top: top + upper + PANEL_GAP, bottom: 4, left: gridLeft, right, containLabel: true }
|
|
5453
|
+
],
|
|
5454
|
+
xAxis: [barX, lineX],
|
|
5455
|
+
yAxis: [
|
|
5456
|
+
{ ...barOpt.yAxis, id: "y-bars", gridIndex: 0 },
|
|
5457
|
+
{ ...lineOpt.yAxis, id: "y-line", gridIndex: 1 }
|
|
5458
|
+
],
|
|
5459
|
+
series: [
|
|
5460
|
+
...barOpt.series.map((s) => ({ ...s, xAxisIndex: 0, yAxisIndex: 0 })),
|
|
5461
|
+
...lineOpt.series.map((s) => ({ ...s, xAxisIndex: 1, yAxisIndex: 1 }))
|
|
5462
|
+
],
|
|
5463
|
+
tooltip: {
|
|
5464
|
+
...barOpt.tooltip,
|
|
5465
|
+
show: showTooltip,
|
|
5466
|
+
trigger: "axis",
|
|
5467
|
+
axisPointer: { type: "line", link: [{ xAxisIndex: "all" }] },
|
|
5468
|
+
formatter: tooltipFormatter
|
|
5469
|
+
},
|
|
5470
|
+
axisPointer: { link: [{ xAxisIndex: "all" }] }
|
|
5471
|
+
};
|
|
5472
|
+
return mergeChartOptions(option, cfg.chartOptions);
|
|
5473
|
+
}
|
|
5474
|
+
function PairedChartImpl({
|
|
5475
|
+
data = NO_ROWS12,
|
|
5476
|
+
index,
|
|
5477
|
+
labels,
|
|
5478
|
+
bars,
|
|
5479
|
+
line,
|
|
5480
|
+
title,
|
|
5481
|
+
summary,
|
|
5482
|
+
actions,
|
|
5483
|
+
fileName,
|
|
5484
|
+
isLoading,
|
|
5485
|
+
emptyState,
|
|
5486
|
+
height = DEFAULT_HEIGHT,
|
|
5487
|
+
heightRatio = [3, 2],
|
|
5488
|
+
yAxisWidth = DEFAULT_Y_AXIS_WIDTH,
|
|
5489
|
+
showLegend = true,
|
|
5490
|
+
showTooltip = true,
|
|
5491
|
+
chartOptions,
|
|
5492
|
+
ariaLabel,
|
|
5493
|
+
className,
|
|
5494
|
+
...rest
|
|
5495
|
+
}, ref) {
|
|
5496
|
+
const { ref: frameRef, width: plotWidth, tokens } = useChartSurface(ref);
|
|
5497
|
+
const barKeys = bars?.categories ?? NO_KEYS;
|
|
5498
|
+
const lineKeys = line?.categories ?? NO_KEYS;
|
|
5499
|
+
const locale = useLocale();
|
|
5500
|
+
const t = TEXT14[uiLanguage(locale)];
|
|
5501
|
+
const barFormatter = useMemo(() => resolveValueFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5502
|
+
const lineFormatter = useMemo(() => resolveValueFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5503
|
+
const barAxis = useMemo(() => resolveAxisFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5504
|
+
const lineAxis = useMemo(() => resolveAxisFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5505
|
+
const barTicks = useMemo(() => resolveAxisTickFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5506
|
+
const lineTicks = useMemo(() => resolveAxisTickFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5507
|
+
const palette = useMemo(() => {
|
|
5508
|
+
const p = pairedPalette(barKeys, lineKeys, bars?.colors, line?.colors);
|
|
5509
|
+
return { bars: resolveChartColors(p.bars, tokens), line: resolveChartColors(p.line, tokens) };
|
|
5510
|
+
}, [barKeys, lineKeys, bars?.colors, line?.colors, tokens]);
|
|
5511
|
+
const option = useMemo(
|
|
5512
|
+
() => buildPairedOption({
|
|
5513
|
+
data,
|
|
5514
|
+
index,
|
|
5515
|
+
labels,
|
|
5516
|
+
barKeys,
|
|
5517
|
+
lineKeys,
|
|
5518
|
+
barPalette: palette.bars,
|
|
5519
|
+
linePalette: palette.line,
|
|
5520
|
+
barFormatter,
|
|
5521
|
+
lineFormatter,
|
|
5522
|
+
barAxis,
|
|
5523
|
+
lineAxis,
|
|
5524
|
+
barTicks,
|
|
5525
|
+
lineTicks,
|
|
5526
|
+
type: bars?.type,
|
|
5527
|
+
fitToData: line?.fitToData,
|
|
5528
|
+
barAxisLabel: bars?.yAxisLabel,
|
|
5529
|
+
lineAxisLabel: line?.yAxisLabel,
|
|
5530
|
+
barMax: bars?.maxValue,
|
|
5531
|
+
lineMax: line?.maxValue,
|
|
5532
|
+
lineMin: line?.minValue,
|
|
5533
|
+
height,
|
|
5534
|
+
heightRatio,
|
|
5535
|
+
yAxisWidth,
|
|
5536
|
+
showLegend,
|
|
5537
|
+
showTooltip,
|
|
5538
|
+
reserveActions: Boolean(actions),
|
|
5539
|
+
plotWidth,
|
|
5540
|
+
guideColor: tokens?.mutedForeground ?? void 0,
|
|
5541
|
+
chartOptions,
|
|
5542
|
+
locale
|
|
5543
|
+
}),
|
|
5544
|
+
[
|
|
5545
|
+
data,
|
|
5546
|
+
index,
|
|
5547
|
+
labels,
|
|
5548
|
+
barKeys,
|
|
5549
|
+
lineKeys,
|
|
5550
|
+
palette,
|
|
5551
|
+
barFormatter,
|
|
5552
|
+
lineFormatter,
|
|
5553
|
+
barAxis,
|
|
5554
|
+
lineAxis,
|
|
5555
|
+
barTicks,
|
|
5556
|
+
lineTicks,
|
|
5557
|
+
bars?.type,
|
|
5558
|
+
bars?.yAxisLabel,
|
|
5559
|
+
bars?.maxValue,
|
|
5560
|
+
line?.fitToData,
|
|
5561
|
+
line?.yAxisLabel,
|
|
5562
|
+
line?.maxValue,
|
|
5563
|
+
line?.minValue,
|
|
5564
|
+
height,
|
|
5565
|
+
heightRatio,
|
|
5566
|
+
yAxisWidth,
|
|
5567
|
+
showLegend,
|
|
5568
|
+
showTooltip,
|
|
5569
|
+
actions,
|
|
5570
|
+
plotWidth,
|
|
5571
|
+
tokens,
|
|
5572
|
+
chartOptions,
|
|
5573
|
+
locale
|
|
5574
|
+
]
|
|
5575
|
+
);
|
|
5576
|
+
const dataTable = useMemo(() => {
|
|
5577
|
+
const upper = chartDataTable(labelRows(data, index, labels), index, barKeys, barFormatter, labels, t.item);
|
|
5578
|
+
const lower = chartDataTable(labelRows(data, index, labels), index, lineKeys, lineFormatter, labels, t.item);
|
|
5579
|
+
const times = timeTooltipLabels(data.map((row) => row[index]), locale);
|
|
5580
|
+
return {
|
|
5581
|
+
...upper,
|
|
5582
|
+
columns: [...upper.columns, ...lower.columns.slice(1)],
|
|
5583
|
+
rows: upper.rows.map((r, i) => [times?.[i] ?? r[0] ?? "", ...r.slice(1), ...(lower.rows[i] ?? []).slice(1)])
|
|
5584
|
+
};
|
|
5585
|
+
}, [data, index, labels, barKeys, lineKeys, barFormatter, lineFormatter, t, locale]);
|
|
5586
|
+
const label2 = ariaLabel ?? t.chart([...barKeys, ...lineKeys]);
|
|
5587
|
+
return /* @__PURE__ */ jsx(
|
|
5588
|
+
ChartFrame,
|
|
5589
|
+
{
|
|
5590
|
+
...chartRootProps(rest),
|
|
5591
|
+
ref: frameRef,
|
|
5592
|
+
title,
|
|
5593
|
+
summary: summary === "auto" ? void 0 : summary,
|
|
5594
|
+
isLoading: isLoading ?? false,
|
|
5595
|
+
isEmpty: data.length === 0,
|
|
5596
|
+
emptyState,
|
|
5597
|
+
height,
|
|
5598
|
+
variant: "bar",
|
|
5599
|
+
ariaLabel: label2,
|
|
5600
|
+
className,
|
|
5601
|
+
dataTable,
|
|
5602
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: summary === "auto" ? void 0 : summary } })
|
|
5603
|
+
}
|
|
5604
|
+
);
|
|
5605
|
+
}
|
|
5606
|
+
var PairedChart = forwardRef(PairedChartImpl);
|
|
5607
|
+
PairedChart.displayName = "PairedChart";
|
|
5608
|
+
|
|
5609
|
+
// ../squadkit/registry/lib/distribution.ts
|
|
5610
|
+
function toFinite(value) {
|
|
5611
|
+
const n = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
5612
|
+
return Number.isFinite(n) ? n : null;
|
|
5613
|
+
}
|
|
5614
|
+
function sortedNumbers(values) {
|
|
5615
|
+
const out = [];
|
|
5616
|
+
for (const v of values) {
|
|
5617
|
+
const n = toFinite(v);
|
|
5618
|
+
if (n !== null) out.push(n);
|
|
5619
|
+
}
|
|
5620
|
+
return out.sort((a, b) => a - b);
|
|
5621
|
+
}
|
|
5622
|
+
function quantile(sorted, p) {
|
|
5623
|
+
if (sorted.length === 0) return null;
|
|
5624
|
+
const pos = (sorted.length - 1) * Math.min(1, Math.max(0, p));
|
|
5625
|
+
const lo = Math.floor(pos);
|
|
5626
|
+
const hi = Math.ceil(pos);
|
|
5627
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
|
5628
|
+
}
|
|
5629
|
+
function boxStats(values, whisker = 1.5) {
|
|
5630
|
+
const sorted = sortedNumbers(values);
|
|
5631
|
+
if (sorted.length === 0) return null;
|
|
5632
|
+
const q1 = quantile(sorted, 0.25);
|
|
5633
|
+
const median = quantile(sorted, 0.5);
|
|
5634
|
+
const q3 = quantile(sorted, 0.75);
|
|
5635
|
+
const reach = Math.max(0, whisker) * (q3 - q1);
|
|
5636
|
+
const low = q1 - reach;
|
|
5637
|
+
const high = q3 + reach;
|
|
5638
|
+
const inside = sorted.filter((v) => v >= low && v <= high);
|
|
5639
|
+
return {
|
|
5640
|
+
count: sorted.length,
|
|
5641
|
+
min: sorted[0],
|
|
5642
|
+
max: sorted[sorted.length - 1],
|
|
5643
|
+
q1,
|
|
5644
|
+
median,
|
|
5645
|
+
q3,
|
|
5646
|
+
whiskerLow: inside[0] ?? q1,
|
|
5647
|
+
whiskerHigh: inside[inside.length - 1] ?? q3,
|
|
5648
|
+
outliers: sorted.filter((v) => v < low || v > high)
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
function niceBinWidth(span, target = 10) {
|
|
5652
|
+
if (!(span > 0) || !Number.isFinite(span)) return 1;
|
|
5653
|
+
const raw = span / target;
|
|
5654
|
+
const pow = 10 ** Math.floor(Math.log10(raw));
|
|
5655
|
+
const f = raw / pow;
|
|
5656
|
+
return (f <= 1 ? 1 : f <= 2 ? 2 : f <= 2.5 ? 2.5 : f <= 5 ? 5 : 10) * pow;
|
|
5657
|
+
}
|
|
5658
|
+
var MAX_BINS = 200;
|
|
5659
|
+
function tidy(n, step) {
|
|
5660
|
+
const digits = Math.min(100, Math.max(0, Math.ceil(-Math.log10(step)) + 6));
|
|
5661
|
+
const rounded = Number(n.toFixed(digits));
|
|
5662
|
+
return Number.isFinite(rounded) ? rounded : n;
|
|
5663
|
+
}
|
|
5664
|
+
function histogramBins(values, config = {}) {
|
|
5665
|
+
const sorted = sortedNumbers(values);
|
|
5666
|
+
if (sorted.length === 0) return [];
|
|
5667
|
+
const width = config.binWidth !== void 0 && Number.isFinite(config.binWidth) && config.binWidth > 0 ? config.binWidth : null;
|
|
5668
|
+
const lo = sorted[0];
|
|
5669
|
+
const hi = sorted[sorted.length - 1];
|
|
5670
|
+
const cap = config.max !== void 0 && Number.isFinite(config.max) ? config.max : null;
|
|
5671
|
+
const end = cap !== null ? cap : hi;
|
|
5672
|
+
const first = config.start !== void 0 && Number.isFinite(config.start) ? config.start : null;
|
|
5673
|
+
let step = width ?? niceBinWidth((cap !== null ? Math.min(hi, cap) : hi) - (first ?? lo));
|
|
5674
|
+
const start = first ?? tidy(Math.floor(lo / step) * step, step);
|
|
5675
|
+
const countFor = (w) => Math.ceil((end - start) / w - 1e-9);
|
|
5676
|
+
if (countFor(step) > MAX_BINS) step = niceBinWidth(end - start, MAX_BINS);
|
|
5677
|
+
const n = Math.min(MAX_BINS, Math.max(cap === null ? 1 : 0, countFor(step)));
|
|
5678
|
+
const bins = [];
|
|
5679
|
+
for (let i = 0; i < n; i++) {
|
|
5680
|
+
const from = i === 0 ? start : bins[i - 1].to;
|
|
5681
|
+
let to = tidy(start + (i + 1) * step, step);
|
|
5682
|
+
if (!(to > from)) to = start + (i + 1) * step;
|
|
5683
|
+
if (cap !== null) to = Math.min(to, cap);
|
|
5684
|
+
bins.push({ from, to, count: 0, overflow: false });
|
|
5685
|
+
}
|
|
5686
|
+
const inRange = cap === null ? sorted : sorted.filter((v) => v < cap);
|
|
5687
|
+
const over = cap === null ? [] : sorted.filter((v) => v >= cap);
|
|
5688
|
+
for (const v of inRange) {
|
|
5689
|
+
if (v < start || bins.length === 0) continue;
|
|
5690
|
+
const i = Math.min(bins.length - 1, Math.floor((v - start) / step + 1e-9));
|
|
5691
|
+
bins[i].count += 1;
|
|
5692
|
+
}
|
|
5693
|
+
if (over.length > 0) bins.push({ from: cap, to: Math.max(hi, cap), count: over.length, overflow: true });
|
|
5694
|
+
return bins;
|
|
5695
|
+
}
|
|
5696
|
+
var TEXT15 = {
|
|
5697
|
+
ja: {
|
|
5698
|
+
countUnit: "\u4EF6",
|
|
5699
|
+
atOrAbove: (from) => `${from}\u4EE5\u4E0A`,
|
|
5700
|
+
range: (from, to) => `${from}\u301C${to}`,
|
|
5701
|
+
count: (n, unit) => `${n}${unit}`,
|
|
5702
|
+
countHeader: (unit) => `${unit}\u6570`,
|
|
5703
|
+
share: "\u69CB\u6210\u6BD4",
|
|
5704
|
+
value: "\u5024",
|
|
5705
|
+
valueInSentence: "\u5024",
|
|
5706
|
+
threshold: "\u3057\u304D\u3044\u5024",
|
|
5707
|
+
peak: (total, bin, count, unit) => `${total}${unit}\u306E\u3046\u3061\u6700\u3082\u591A\u3044\u306E\u306F ${bin}\uFF08${count}${unit}\uFF09`,
|
|
5708
|
+
overThreshold: (name, value, count, share) => `${name}\uFF08${value}\uFF09\u4EE5\u4E0A\u306F ${count}\uFF08${share}\uFF09`,
|
|
5709
|
+
sentenceJoin: "\u3002",
|
|
5710
|
+
chart: (label2) => `${label2}\u306E\u30D2\u30B9\u30C8\u30B0\u30E9\u30E0`
|
|
5711
|
+
},
|
|
5712
|
+
en: {
|
|
5713
|
+
countUnit: "",
|
|
5714
|
+
atOrAbove: (from) => `${from}+`,
|
|
5715
|
+
range: (from, to) => `${from}\u2013${to}`,
|
|
5716
|
+
count: (n, unit) => unit ? `${n} ${unit}` : `${n}`,
|
|
5717
|
+
countHeader: () => "Count",
|
|
5718
|
+
share: "Share",
|
|
5719
|
+
value: "Value",
|
|
5720
|
+
valueInSentence: "values",
|
|
5721
|
+
threshold: "Threshold",
|
|
5722
|
+
peak: (total, bin, count, unit) => `Most common: ${bin} (${unit ? `${count} ${unit}` : count} of ${total})`,
|
|
5723
|
+
overThreshold: (name, value, count, share) => `At or above ${name} (${value}): ${count} (${share})`,
|
|
5724
|
+
sentenceJoin: ". ",
|
|
5725
|
+
chart: (label2) => `Histogram of ${label2}`
|
|
5726
|
+
}
|
|
5727
|
+
};
|
|
5728
|
+
var textOf4 = (locale) => TEXT15[uiLanguage(resolveLocale(locale))];
|
|
5729
|
+
function binLabel(bin, formatter, locale) {
|
|
5730
|
+
const t = textOf4(locale);
|
|
5731
|
+
return bin.overflow ? t.atOrAbove(formatter(bin.from)) : t.range(formatter(bin.from), formatter(bin.to));
|
|
5732
|
+
}
|
|
5733
|
+
function thresholdPosition(bins, value) {
|
|
5734
|
+
const lastIndex = bins.length - 1;
|
|
5735
|
+
const i = bins.findIndex((b, k) => k === lastIndex ? value >= b.from && (b.overflow || value <= b.to) : value >= b.from && value < b.to);
|
|
5736
|
+
const bin = bins[i];
|
|
5737
|
+
if (!bin) return null;
|
|
5738
|
+
return bin.overflow ? i - 0.5 : i - 0.5 + (value - bin.from) / (bin.to - bin.from);
|
|
5739
|
+
}
|
|
5740
|
+
function countAtOrAbove(values, value, from = -Infinity) {
|
|
5741
|
+
return sortedNumbers(values).filter((v) => v >= value && v >= from).length;
|
|
5742
|
+
}
|
|
5743
|
+
var percent = (n, total) => total === 0 ? "0%" : `${Math.round(n / total * 100)}%`;
|
|
5744
|
+
function buildHistogramSummary(bins, formatter, config) {
|
|
5745
|
+
if (bins.length === 0 || config.total === 0) return void 0;
|
|
5746
|
+
const t = textOf4(config.locale);
|
|
5747
|
+
const count = (n) => t.count(n, config.countUnit);
|
|
5748
|
+
const peak = bins.reduce((a, b) => b.count > a.count ? b : a);
|
|
5749
|
+
const parts = [t.peak(config.total, binLabel(peak, formatter, config.locale), peak.count, config.countUnit)];
|
|
5750
|
+
if (config.threshold && config.atOrAbove !== void 0) {
|
|
5751
|
+
const name = config.threshold.label ?? t.threshold;
|
|
5752
|
+
parts.push(t.overThreshold(name, formatter(config.threshold.value), count(config.atOrAbove), percent(config.atOrAbove, config.total)));
|
|
5753
|
+
}
|
|
5754
|
+
return parts.join(t.sentenceJoin);
|
|
5755
|
+
}
|
|
5756
|
+
function histogramDataTable(bins, formatter, config) {
|
|
5757
|
+
const t = textOf4(config.locale);
|
|
5758
|
+
return {
|
|
5759
|
+
columns: [config.valueLabel ?? t.value, t.countHeader(config.countUnit), t.share],
|
|
5760
|
+
rows: bins.map((b) => [binLabel(b, formatter, config.locale), `${b.count}`, percent(b.count, config.total)])
|
|
5761
|
+
};
|
|
5762
|
+
}
|
|
5763
|
+
function thresholdPoint(bins, value) {
|
|
5764
|
+
const pos = thresholdPosition(bins, value) ?? 0;
|
|
5765
|
+
const index = Math.min(bins.length - 1, Math.max(0, Math.round(pos)));
|
|
5766
|
+
return [index, 0, pos - index];
|
|
5767
|
+
}
|
|
5768
|
+
function thresholdRenderer(style) {
|
|
5769
|
+
return (params, api) => {
|
|
5770
|
+
const { x: gx, y: gy, width, height } = params.coordSys;
|
|
5771
|
+
const band = Math.abs(api.size([1, 0])[0] ?? 0);
|
|
5772
|
+
const x = (api.coord([api.value(0), 0])[0] ?? 0) + api.value(2) * band;
|
|
5773
|
+
const nearRight = x > gx + width * 0.7;
|
|
5774
|
+
return {
|
|
5775
|
+
type: "group",
|
|
5776
|
+
children: [
|
|
5777
|
+
{
|
|
5778
|
+
type: "line",
|
|
5779
|
+
shape: { x1: x, y1: gy, x2: x, y2: gy + height },
|
|
5780
|
+
style: { stroke: style.lineColor || "#888", lineWidth: 1.5, lineDash: [4, 4], fill: "none" }
|
|
5781
|
+
},
|
|
5782
|
+
{
|
|
5783
|
+
type: "text",
|
|
5784
|
+
style: {
|
|
5785
|
+
text: style.text,
|
|
5786
|
+
x,
|
|
5787
|
+
y: gy - 4,
|
|
5788
|
+
fill: style.labelColor || "#888",
|
|
5789
|
+
textAlign: nearRight ? "right" : "left",
|
|
5790
|
+
textVerticalAlign: "bottom"
|
|
5791
|
+
}
|
|
5792
|
+
}
|
|
5793
|
+
]
|
|
5794
|
+
};
|
|
5795
|
+
};
|
|
5796
|
+
}
|
|
5797
|
+
var ROW_STYLE3 = "display:flex;align-items:center;gap:8px;line-height:20px";
|
|
5798
|
+
function buildHistogramOption(config) {
|
|
5799
|
+
const { locale } = config;
|
|
5800
|
+
const t = textOf4(locale);
|
|
5801
|
+
const { bins, total, colors, valueFormatter, threshold, showTooltip = true, countUnit = t.countUnit } = config;
|
|
5802
|
+
const pos = threshold ? thresholdPosition(bins, threshold.value) : null;
|
|
5803
|
+
const option = {
|
|
5804
|
+
...chartMotion(false),
|
|
5805
|
+
legend: { show: false },
|
|
5806
|
+
grid: { top: pos === null ? 16 : 28, right: 16, bottom: 4, left: 4, containLabel: true },
|
|
5807
|
+
tooltip: {
|
|
5808
|
+
show: showTooltip,
|
|
5809
|
+
trigger: "axis",
|
|
5810
|
+
axisPointer: { type: "shadow" },
|
|
5811
|
+
formatter: (params) => {
|
|
5812
|
+
const list = Array.isArray(params) ? params : [params];
|
|
5813
|
+
const bin = bins[(list.find((p) => p.seriesId === "count") ?? list[0])?.dataIndex ?? 0];
|
|
5814
|
+
if (!bin) return "";
|
|
5815
|
+
const color = escapeHtml((bin.overflow ? colors.overflow : colors.bar) || "currentColor");
|
|
5816
|
+
const line = (name, text) => `<div style="${ROW_STYLE3}"><span style="display:inline-block;width:8px;height:8px;border-radius:9999px;background:${color}"></span><span style="flex:1;opacity:.8">${escapeHtml(name)}</span><span style="font-weight:500;font-variant-numeric:tabular-nums;margin-left:12px">${escapeHtml(text)}</span></div>`;
|
|
5817
|
+
return `<div style="font-weight:500;margin-bottom:4px">${escapeHtml(binLabel(bin, valueFormatter, locale))}</div>` + line(t.countHeader(countUnit), t.count(bin.count, countUnit)) + line(t.share, percent(bin.count, total));
|
|
5818
|
+
}
|
|
5819
|
+
},
|
|
5820
|
+
xAxis: {
|
|
5821
|
+
type: "category",
|
|
5822
|
+
data: bins.map((b) => binLabel(b, valueFormatter, locale)),
|
|
5823
|
+
axisTick: { show: false },
|
|
5824
|
+
axisLabel: { hideOverlap: true }
|
|
5825
|
+
},
|
|
5826
|
+
yAxis: {
|
|
5827
|
+
type: "value",
|
|
5828
|
+
minInterval: 1,
|
|
5829
|
+
axisLine: { show: false },
|
|
5830
|
+
axisTick: { show: false },
|
|
5831
|
+
splitLine: { show: true }
|
|
5832
|
+
},
|
|
5833
|
+
series: [
|
|
5834
|
+
{
|
|
5835
|
+
id: "count",
|
|
5836
|
+
type: "bar",
|
|
5837
|
+
barCategoryGap: "6%",
|
|
5838
|
+
data: bins.map((b) => ({
|
|
5839
|
+
value: b.count,
|
|
5840
|
+
itemStyle: { color: (b.overflow ? colors.overflow : colors.bar) || void 0, borderRadius: [2, 2, 0, 0] }
|
|
5841
|
+
})),
|
|
5842
|
+
label: { show: false, color: config.labelColor, fontVariantNumeric: "tabular-nums" }
|
|
5843
|
+
},
|
|
5844
|
+
...threshold && pos !== null ? [
|
|
5845
|
+
{
|
|
5846
|
+
id: "threshold",
|
|
5847
|
+
type: "custom",
|
|
5848
|
+
silent: true,
|
|
5849
|
+
z: 10,
|
|
5850
|
+
animation: false,
|
|
5851
|
+
tooltip: { show: false },
|
|
5852
|
+
encode: { x: 0, y: 1 },
|
|
5853
|
+
// [階級の添字, 0(y の位置は使わない), 階級の中での位置(左端 -0.5 〜 右端 0.5)]
|
|
5854
|
+
data: [thresholdPoint(bins, threshold.value)],
|
|
5855
|
+
renderItem: thresholdRenderer({
|
|
5856
|
+
text: `${threshold.label ?? t.threshold} ${valueFormatter(threshold.value)}`,
|
|
5857
|
+
lineColor: config.lineColor,
|
|
5858
|
+
labelColor: config.labelColor
|
|
5859
|
+
})
|
|
5860
|
+
}
|
|
5861
|
+
] : []
|
|
5862
|
+
]
|
|
5863
|
+
};
|
|
5864
|
+
return mergeChartOptions(option, config.chartOptions);
|
|
5865
|
+
}
|
|
5866
|
+
var NO_VALUES = [];
|
|
5867
|
+
var HistogramChart = forwardRef(function HistogramChart2({
|
|
5868
|
+
values = NO_VALUES,
|
|
5869
|
+
binWidth,
|
|
5870
|
+
start,
|
|
5871
|
+
max,
|
|
5872
|
+
threshold,
|
|
5873
|
+
countUnit: countUnitProp,
|
|
5874
|
+
valueLabel,
|
|
5875
|
+
format,
|
|
5876
|
+
valueFormatter: valueFormatterProp,
|
|
5877
|
+
showTooltip = true,
|
|
5878
|
+
title,
|
|
5879
|
+
summary,
|
|
5880
|
+
actions,
|
|
5881
|
+
fileName,
|
|
5882
|
+
isLoading,
|
|
5883
|
+
emptyState,
|
|
5884
|
+
height,
|
|
5885
|
+
chartOptions,
|
|
5886
|
+
ariaLabel,
|
|
5887
|
+
className,
|
|
5888
|
+
...rest
|
|
5889
|
+
}, ref) {
|
|
5890
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
5891
|
+
const locale = useLocale();
|
|
5892
|
+
const t = TEXT15[uiLanguage(locale)];
|
|
5893
|
+
const countUnit = countUnitProp ?? t.countUnit;
|
|
5894
|
+
const valueFormatter = useMemo(
|
|
5895
|
+
() => valueFormatterProp ?? (format === void 0 ? toValueFormatter("number", locale) : resolveValueFormatter(void 0, format, locale)),
|
|
5896
|
+
[valueFormatterProp, format, locale]
|
|
5897
|
+
);
|
|
5898
|
+
const bins = useMemo(() => histogramBins(values, { binWidth, start, max }), [values, binWidth, start, max]);
|
|
5899
|
+
const total = useMemo(() => bins.reduce((sum, b) => sum + b.count, 0), [bins]);
|
|
5900
|
+
const colors = useMemo(() => {
|
|
5901
|
+
const [bar = ""] = resolveChartColors(["chart-1"], tokens);
|
|
5902
|
+
return { bar, overflow: resolveFillColors(tokens).neutral };
|
|
5903
|
+
}, [tokens]);
|
|
5904
|
+
const option = useMemo(
|
|
5905
|
+
() => buildHistogramOption({
|
|
5906
|
+
bins,
|
|
5907
|
+
total,
|
|
5908
|
+
colors,
|
|
5909
|
+
valueFormatter,
|
|
5910
|
+
countUnit,
|
|
5911
|
+
threshold,
|
|
5912
|
+
showTooltip,
|
|
5913
|
+
labelColor: tokens?.foreground ?? void 0,
|
|
5914
|
+
lineColor: tokens?.mutedForeground ?? void 0,
|
|
5915
|
+
chartOptions,
|
|
5916
|
+
locale
|
|
5917
|
+
}),
|
|
5918
|
+
[bins, total, colors, valueFormatter, countUnit, threshold, showTooltip, tokens, chartOptions, locale]
|
|
5919
|
+
);
|
|
5920
|
+
const dataTable = useMemo(
|
|
5921
|
+
() => histogramDataTable(bins, valueFormatter, { total, countUnit, valueLabel, locale }),
|
|
5922
|
+
[bins, valueFormatter, total, countUnit, valueLabel, locale]
|
|
5923
|
+
);
|
|
5924
|
+
const resolvedSummary = useMemo(
|
|
5925
|
+
() => summary === "auto" ? buildHistogramSummary(bins, valueFormatter, {
|
|
5926
|
+
total,
|
|
5927
|
+
countUnit,
|
|
5928
|
+
threshold,
|
|
5929
|
+
atOrAbove: threshold ? countAtOrAbove(values, threshold.value, bins[0]?.from) : void 0,
|
|
5930
|
+
locale
|
|
5931
|
+
}) : summary,
|
|
5932
|
+
[summary, bins, valueFormatter, total, countUnit, threshold, values, locale]
|
|
5933
|
+
);
|
|
5934
|
+
const label2 = ariaLabel ?? t.chart(valueLabel ?? t.valueInSentence);
|
|
5935
|
+
return /* @__PURE__ */ jsx(
|
|
5936
|
+
ChartFrame,
|
|
5937
|
+
{
|
|
5938
|
+
...chartRootProps(rest),
|
|
5939
|
+
ref: frameRef,
|
|
5940
|
+
title,
|
|
5941
|
+
summary: resolvedSummary,
|
|
5942
|
+
isLoading: isLoading ?? false,
|
|
5943
|
+
isEmpty: bins.length === 0,
|
|
5944
|
+
emptyState,
|
|
5945
|
+
height,
|
|
5946
|
+
variant: "bar",
|
|
5947
|
+
ariaLabel: label2,
|
|
5948
|
+
className,
|
|
5949
|
+
dataTable,
|
|
5950
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } })
|
|
5951
|
+
}
|
|
5952
|
+
);
|
|
5953
|
+
});
|
|
5954
|
+
HistogramChart.displayName = "HistogramChart";
|
|
5955
|
+
var TEXT16 = {
|
|
5956
|
+
ja: {
|
|
5957
|
+
value: "\u5024",
|
|
5958
|
+
valueInSentence: "\u5024",
|
|
5959
|
+
medianOf: (name, label2, median) => `${name}\u306E${label2}\u306E\u4E2D\u592E\u5024\u306F ${median}`,
|
|
5960
|
+
medianRange: (label2, top, topMedian, low, lowMedian) => `${label2}\u306E\u4E2D\u592E\u5024\u304C\u6700\u3082\u9AD8\u3044\u306E\u306F ${top}\uFF08${topMedian}\uFF09\u3001\u6700\u3082\u4F4E\u3044\u306E\u306F ${low}\uFF08${lowMedian}\uFF09`,
|
|
5961
|
+
outliers: (n) => `\u5916\u308C\u5024\u306F ${n} \u4EF6`,
|
|
5962
|
+
sentenceJoin: "\u3002",
|
|
5963
|
+
columns: ["\u7FA4", "\u4EF6\u6570", "\u6700\u5C0F\uFF08\u3072\u3052\uFF09", "\u7B2C1\u56DB\u5206\u4F4D", "\u4E2D\u592E\u5024", "\u7B2C3\u56DB\u5206\u4F4D", "\u6700\u5927\uFF08\u3072\u3052\uFF09", "\u5916\u308C\u5024"],
|
|
5964
|
+
listJoin: "\u3001",
|
|
5965
|
+
none: "\u306A\u3057",
|
|
5966
|
+
tooltipTitle: (name, label2, count) => `${name}\uFF08${label2}\u30FB${count}\u4EF6\uFF09`,
|
|
5967
|
+
max: "\u6700\u5927\uFF08\u3072\u3052\uFF09",
|
|
5968
|
+
q3: "\u7B2C3\u56DB\u5206\u4F4D",
|
|
5969
|
+
median: "\u4E2D\u592E\u5024",
|
|
5970
|
+
q1: "\u7B2C1\u56DB\u5206\u4F4D",
|
|
5971
|
+
min: "\u6700\u5C0F\uFF08\u3072\u3052\uFF09",
|
|
5972
|
+
outlierRow: "\u5916\u308C\u5024",
|
|
5973
|
+
chart: (label2) => `${label2}\u306E\u7BB1\u3072\u3052\u56F3`
|
|
5974
|
+
},
|
|
5975
|
+
en: {
|
|
5976
|
+
value: "Value",
|
|
5977
|
+
valueInSentence: "value",
|
|
5978
|
+
medianOf: (name, label2, median) => `Median ${label2} for ${name}: ${median}`,
|
|
5979
|
+
medianRange: (label2, top, topMedian, low, lowMedian) => `Highest median ${label2}: ${top} (${topMedian}); lowest: ${low} (${lowMedian})`,
|
|
5980
|
+
outliers: (n) => n === 1 ? "1 outlier" : `${n} outliers`,
|
|
5981
|
+
sentenceJoin: ". ",
|
|
5982
|
+
columns: ["Group", "Count", "Min (whisker)", "Q1", "Median", "Q3", "Max (whisker)", "Outliers"],
|
|
5983
|
+
listJoin: ", ",
|
|
5984
|
+
none: "None",
|
|
5985
|
+
tooltipTitle: (name, label2, count) => `${name} (${label2}, n=${count})`,
|
|
5986
|
+
max: "Max (whisker)",
|
|
5987
|
+
q3: "Q3",
|
|
5988
|
+
median: "Median",
|
|
5989
|
+
q1: "Q1",
|
|
5990
|
+
min: "Min (whisker)",
|
|
5991
|
+
outlierRow: "Outliers",
|
|
5992
|
+
chart: (label2) => `Box plot of ${label2}`
|
|
5993
|
+
}
|
|
5994
|
+
};
|
|
5995
|
+
var textOf5 = (locale) => TEXT16[uiLanguage(resolveLocale(locale))];
|
|
5996
|
+
function boxPlotEntries(groups, whisker) {
|
|
5997
|
+
return groups.map((g) => ({ name: g.name, stats: boxStats(g.values, whisker) }));
|
|
5998
|
+
}
|
|
5999
|
+
function buildBoxPlotSummary(entries, formatter, valueLabel, locale) {
|
|
6000
|
+
const t = textOf5(locale);
|
|
6001
|
+
const label2 = valueLabel ?? t.valueInSentence;
|
|
6002
|
+
const real = entries.filter((e) => e.stats !== null);
|
|
6003
|
+
if (real.length === 0) return void 0;
|
|
6004
|
+
const top = real.reduce((a, b) => b.stats.median > a.stats.median ? b : a);
|
|
6005
|
+
const low = real.reduce((a, b) => b.stats.median < a.stats.median ? b : a);
|
|
6006
|
+
const parts = [
|
|
6007
|
+
real.length === 1 ? t.medianOf(top.name, label2, formatter(top.stats.median)) : t.medianRange(label2, top.name, formatter(top.stats.median), low.name, formatter(low.stats.median))
|
|
6008
|
+
];
|
|
6009
|
+
const outliers = real.reduce((sum, e) => sum + e.stats.outliers.length, 0);
|
|
6010
|
+
if (outliers > 0) parts.push(t.outliers(outliers));
|
|
6011
|
+
return parts.join(t.sentenceJoin);
|
|
6012
|
+
}
|
|
6013
|
+
function boxPlotDataTable(entries, formatter, locale) {
|
|
6014
|
+
const t = textOf5(locale);
|
|
6015
|
+
const f = (n) => n === void 0 ? EMPTY_VALUE : formatter(n);
|
|
6016
|
+
return {
|
|
6017
|
+
columns: [...t.columns],
|
|
6018
|
+
rows: entries.map(({ name, stats: s }) => [
|
|
6019
|
+
name,
|
|
6020
|
+
s ? `${s.count}` : EMPTY_VALUE,
|
|
6021
|
+
f(s?.whiskerLow),
|
|
6022
|
+
f(s?.q1),
|
|
6023
|
+
f(s?.median),
|
|
6024
|
+
f(s?.q3),
|
|
6025
|
+
f(s?.whiskerHigh),
|
|
6026
|
+
s ? s.outliers.length > 0 ? s.outliers.map(formatter).join(t.listJoin) : t.none : EMPTY_VALUE
|
|
6027
|
+
])
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
var ROW_STYLE4 = "display:flex;align-items:center;gap:8px;line-height:20px";
|
|
6031
|
+
var BOX_SHARE = 0.4;
|
|
6032
|
+
function boxPlotRenderer(color, outliers) {
|
|
6033
|
+
return (_params, api) => {
|
|
6034
|
+
const x = api.coord([api.value(0), api.value(3)])[0] ?? 0;
|
|
6035
|
+
const y = (dim) => api.coord([api.value(0), api.value(dim)])[1] ?? 0;
|
|
6036
|
+
const band = Math.abs(api.size([1, 0])[0] ?? 0);
|
|
6037
|
+
const half = band * BOX_SHARE / 2;
|
|
6038
|
+
const cap = half / 2;
|
|
6039
|
+
const [yLow, yQ1, yMed, yQ3, yHigh] = [y(1), y(2), y(3), y(4), y(5)];
|
|
6040
|
+
const stroke = { stroke: color, lineWidth: 1.5, fill: "none" };
|
|
6041
|
+
const line = (x1, y1, x2, y2) => ({ type: "line", shape: { x1, y1, x2, y2 }, style: stroke });
|
|
6042
|
+
return {
|
|
6043
|
+
type: "group",
|
|
6044
|
+
children: [
|
|
6045
|
+
line(x, yHigh, x, yQ3),
|
|
6046
|
+
line(x, yQ1, x, yLow),
|
|
6047
|
+
line(x - cap, yHigh, x + cap, yHigh),
|
|
6048
|
+
line(x - cap, yLow, x + cap, yLow),
|
|
6049
|
+
{
|
|
6050
|
+
type: "rect",
|
|
6051
|
+
shape: { x: x - half, y: Math.min(yQ3, yQ1), width: half * 2, height: Math.abs(yQ1 - yQ3), r: 2 },
|
|
6052
|
+
style: { fill: withAlpha(color, 0.25), stroke: color, lineWidth: 1.5 }
|
|
6053
|
+
},
|
|
6054
|
+
line(x - half, yMed, x + half, yMed),
|
|
6055
|
+
...(outliers[api.value(0)] ?? []).map((v) => ({
|
|
6056
|
+
type: "circle",
|
|
6057
|
+
shape: { cx: x, cy: api.coord([api.value(0), v])[1] ?? 0, r: 3 },
|
|
6058
|
+
style: { fill: "transparent", stroke: color, lineWidth: 1.5 }
|
|
6059
|
+
}))
|
|
6060
|
+
]
|
|
6061
|
+
};
|
|
6062
|
+
};
|
|
6063
|
+
}
|
|
6064
|
+
function buildBoxPlotOption(config) {
|
|
6065
|
+
const t = textOf5(config.locale);
|
|
6066
|
+
const { entries, valueFormatter, showTooltip = true, valueLabel = t.value } = config;
|
|
6067
|
+
const axisFormatter = config.axisFormatter ?? valueFormatter;
|
|
6068
|
+
const color = config.color || "#888";
|
|
6069
|
+
const data = entries.flatMap((e, i) => e.stats ? [[i, e.stats.whiskerLow, e.stats.q1, e.stats.median, e.stats.q3, e.stats.whiskerHigh, e.stats.min, e.stats.max]] : []);
|
|
6070
|
+
const entryAt = (i) => entries[i];
|
|
6071
|
+
const option = {
|
|
6072
|
+
...chartMotion(false),
|
|
6073
|
+
legend: { show: false },
|
|
6074
|
+
grid: { top: 16, right: 16, bottom: 4, left: 4, containLabel: true },
|
|
6075
|
+
tooltip: {
|
|
6076
|
+
show: showTooltip,
|
|
6077
|
+
trigger: "item",
|
|
6078
|
+
formatter: (params) => {
|
|
6079
|
+
const entry = entryAt(params.value?.[0] ?? -1);
|
|
6080
|
+
const s = entry?.stats;
|
|
6081
|
+
if (!entry || !s) return "";
|
|
6082
|
+
const line = (name, text) => `<div style="${ROW_STYLE4}"><span style="flex:1;opacity:.8">${escapeHtml(name)}</span><span style="font-weight:500;font-variant-numeric:tabular-nums;margin-left:12px">${escapeHtml(text)}</span></div>`;
|
|
6083
|
+
return `<div style="font-weight:500;margin-bottom:4px">${escapeHtml(t.tooltipTitle(entry.name, valueLabel, s.count))}</div>` + line(t.max, valueFormatter(s.whiskerHigh)) + line(t.q3, valueFormatter(s.q3)) + line(t.median, valueFormatter(s.median)) + line(t.q1, valueFormatter(s.q1)) + line(t.min, valueFormatter(s.whiskerLow)) + (s.outliers.length > 0 ? line(t.outlierRow, s.outliers.map(valueFormatter).join(t.listJoin)) : "");
|
|
6084
|
+
}
|
|
6085
|
+
},
|
|
6086
|
+
xAxis: { type: "category", data: entries.map((e) => e.name), axisTick: { show: false }, axisLabel: { interval: 0, hideOverlap: false } },
|
|
6087
|
+
yAxis: {
|
|
6088
|
+
type: "value",
|
|
6089
|
+
axisLine: { show: false },
|
|
6090
|
+
axisTick: { show: false },
|
|
6091
|
+
splitLine: { show: true },
|
|
6092
|
+
axisLabel: { formatter: (v) => axisFormatter(v) }
|
|
6093
|
+
},
|
|
6094
|
+
series: [
|
|
6095
|
+
{
|
|
6096
|
+
id: "box",
|
|
6097
|
+
type: "custom",
|
|
6098
|
+
encode: { x: 0, y: [1, 2, 3, 4, 5, 6, 7] },
|
|
6099
|
+
data,
|
|
6100
|
+
renderItem: boxPlotRenderer(color, entries.map((e) => e.stats?.outliers ?? []))
|
|
6101
|
+
}
|
|
6102
|
+
]
|
|
6103
|
+
};
|
|
6104
|
+
return mergeChartOptions(option, config.chartOptions);
|
|
6105
|
+
}
|
|
6106
|
+
var NO_GROUPS = [];
|
|
6107
|
+
var BoxPlotChart = forwardRef(function BoxPlotChart2({
|
|
6108
|
+
groups = NO_GROUPS,
|
|
6109
|
+
whisker,
|
|
6110
|
+
valueLabel,
|
|
6111
|
+
format,
|
|
6112
|
+
valueFormatter: valueFormatterProp,
|
|
6113
|
+
showTooltip = true,
|
|
6114
|
+
title,
|
|
6115
|
+
summary,
|
|
6116
|
+
actions,
|
|
6117
|
+
fileName,
|
|
6118
|
+
isLoading,
|
|
6119
|
+
emptyState,
|
|
6120
|
+
height,
|
|
6121
|
+
chartOptions,
|
|
6122
|
+
ariaLabel,
|
|
6123
|
+
className,
|
|
6124
|
+
...rest
|
|
6125
|
+
}, ref) {
|
|
6126
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
6127
|
+
const locale = useLocale();
|
|
6128
|
+
const t = TEXT16[uiLanguage(locale)];
|
|
6129
|
+
const valueFormatter = useMemo(() => resolveValueFormatter(valueFormatterProp, format, locale), [valueFormatterProp, format, locale]);
|
|
6130
|
+
const entries = useMemo(() => boxPlotEntries(groups, whisker), [groups, whisker]);
|
|
6131
|
+
const [color = ""] = useMemo(() => resolveChartColors(["chart-1"], tokens), [tokens]);
|
|
6132
|
+
const option = useMemo(
|
|
6133
|
+
() => buildBoxPlotOption({ entries, color, valueFormatter, valueLabel, showTooltip, labelColor: tokens?.foreground ?? void 0, chartOptions, locale }),
|
|
6134
|
+
[entries, color, valueFormatter, valueLabel, showTooltip, tokens, chartOptions, locale]
|
|
6135
|
+
);
|
|
6136
|
+
const dataTable = useMemo(() => boxPlotDataTable(entries, valueFormatter, locale), [entries, valueFormatter, locale]);
|
|
6137
|
+
const resolvedSummary = useMemo(
|
|
6138
|
+
() => summary === "auto" ? buildBoxPlotSummary(entries, valueFormatter, valueLabel, locale) : summary,
|
|
6139
|
+
[summary, entries, valueFormatter, valueLabel, locale]
|
|
6140
|
+
);
|
|
6141
|
+
const label2 = ariaLabel ?? t.chart(valueLabel ?? t.valueInSentence);
|
|
6142
|
+
return /* @__PURE__ */ jsx(
|
|
6143
|
+
ChartFrame,
|
|
6144
|
+
{
|
|
6145
|
+
...chartRootProps(rest),
|
|
6146
|
+
ref: frameRef,
|
|
6147
|
+
title,
|
|
6148
|
+
summary: resolvedSummary,
|
|
6149
|
+
isLoading: isLoading ?? false,
|
|
6150
|
+
isEmpty: entries.length === 0,
|
|
6151
|
+
emptyState,
|
|
6152
|
+
height,
|
|
6153
|
+
variant: "bar",
|
|
6154
|
+
ariaLabel: label2,
|
|
6155
|
+
className,
|
|
6156
|
+
dataTable,
|
|
6157
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } })
|
|
6158
|
+
}
|
|
6159
|
+
);
|
|
6160
|
+
});
|
|
6161
|
+
BoxPlotChart.displayName = "BoxPlotChart";
|
|
6162
|
+
var STATUS_LABELS = {
|
|
6163
|
+
ja: {
|
|
6164
|
+
success: "\u6B63\u5E38",
|
|
6165
|
+
partial: "\u4E00\u90E8\u5931\u6557",
|
|
6166
|
+
failed: "\u5931\u6557",
|
|
6167
|
+
delayed: "\u9045\u5EF6",
|
|
6168
|
+
running: "\u5B9F\u884C\u4E2D",
|
|
6169
|
+
stopped: "\u505C\u6B62\u4E2D",
|
|
6170
|
+
active: "\u6709\u52B9",
|
|
6171
|
+
inactive: "\u7121\u52B9",
|
|
6172
|
+
warning: "\u8B66\u544A",
|
|
6173
|
+
error: "\u30A8\u30E9\u30FC",
|
|
6174
|
+
pending: "\u4FDD\u7559\u4E2D"
|
|
6175
|
+
},
|
|
6176
|
+
en: {
|
|
6177
|
+
success: "Healthy",
|
|
6178
|
+
partial: "Partially failed",
|
|
6179
|
+
failed: "Failed",
|
|
6180
|
+
delayed: "Delayed",
|
|
6181
|
+
running: "Running",
|
|
6182
|
+
stopped: "Stopped",
|
|
6183
|
+
active: "Active",
|
|
6184
|
+
inactive: "Inactive",
|
|
6185
|
+
warning: "Warning",
|
|
6186
|
+
error: "Error",
|
|
6187
|
+
pending: "Pending"
|
|
6188
|
+
}
|
|
6189
|
+
};
|
|
6190
|
+
var STATUS_VOCABULARY = {
|
|
6191
|
+
success: { label: STATUS_LABELS.en.success, tone: "positive" },
|
|
6192
|
+
partial: { label: STATUS_LABELS.en.partial, tone: "warning" },
|
|
6193
|
+
failed: { label: STATUS_LABELS.en.failed, tone: "negative" },
|
|
6194
|
+
delayed: { label: STATUS_LABELS.en.delayed, tone: "warning" },
|
|
6195
|
+
running: { label: STATUS_LABELS.en.running, tone: "info" },
|
|
6196
|
+
stopped: { label: STATUS_LABELS.en.stopped, tone: "neutral" },
|
|
6197
|
+
active: { label: STATUS_LABELS.en.active, tone: "positive" },
|
|
6198
|
+
inactive: { label: STATUS_LABELS.en.inactive, tone: "neutral" },
|
|
6199
|
+
warning: { label: STATUS_LABELS.en.warning, tone: "warning" },
|
|
6200
|
+
error: { label: STATUS_LABELS.en.error, tone: "negative" },
|
|
6201
|
+
pending: { label: STATUS_LABELS.en.pending, tone: "info" }
|
|
6202
|
+
};
|
|
6203
|
+
function statusLabel(status, locale) {
|
|
6204
|
+
return Object.hasOwn(STATUS_VOCABULARY, status) ? STATUS_LABELS[uiLanguage(resolveLocale(locale))][status] : status;
|
|
6205
|
+
}
|
|
6206
|
+
var defaultColorMap = Object.fromEntries(
|
|
6207
|
+
Object.entries(STATUS_VOCABULARY).map(([key, { tone }]) => [key, tone])
|
|
6208
|
+
);
|
|
6209
|
+
var legacyColorTones = {
|
|
6210
|
+
green: "positive",
|
|
6211
|
+
gray: "neutral",
|
|
6212
|
+
yellow: "warning",
|
|
6213
|
+
red: "negative",
|
|
6214
|
+
blue: "info"
|
|
6215
|
+
};
|
|
6216
|
+
var toneStyles = {
|
|
6217
|
+
positive: { dot: "bg-positive", bg: "bg-positive-subtle", text: "text-positive", border: "border-positive/20 dark:border-positive/25" },
|
|
6218
|
+
negative: { dot: "bg-negative", bg: "bg-negative-subtle", text: "text-negative", border: "border-negative/20 dark:border-negative/25" },
|
|
6219
|
+
warning: { dot: "bg-warning", bg: "bg-warning-subtle", text: "text-warning", border: "border-warning/20 dark:border-warning/25" },
|
|
6220
|
+
info: { dot: "bg-info", bg: "bg-info-subtle", text: "text-info", border: "border-info/20 dark:border-info/25" },
|
|
6221
|
+
neutral: { dot: "bg-neutral", bg: "bg-neutral-subtle", text: "text-neutral", border: "border-neutral/20 dark:border-neutral/25" }
|
|
6222
|
+
};
|
|
6223
|
+
var resolveTone = (color) => {
|
|
6224
|
+
if (color === void 0) return "neutral";
|
|
6225
|
+
if (Object.hasOwn(toneStyles, color)) return color;
|
|
6226
|
+
return legacyColorTones[color] ?? "neutral";
|
|
6227
|
+
};
|
|
6228
|
+
var StatusBadge = forwardRef(function StatusBadge2({ status, label: label2, colorMap, className }, ref) {
|
|
6229
|
+
const locale = useLocale();
|
|
6230
|
+
const mergedColorMap = { ...defaultColorMap, ...colorMap };
|
|
6231
|
+
const style = toneStyles[resolveTone(mergedColorMap[status])];
|
|
6232
|
+
const displayLabel = label2 ?? statusLabel(status, locale);
|
|
6233
|
+
return /* @__PURE__ */ jsxs(
|
|
6234
|
+
Badge,
|
|
6235
|
+
{
|
|
6236
|
+
ref,
|
|
6237
|
+
"data-slot": "status-badge",
|
|
6238
|
+
variant: "outline",
|
|
6239
|
+
className: cn("gap-1.5 rounded-md", style.border, style.bg, style.text, className),
|
|
6240
|
+
"aria-label": displayLabel,
|
|
6241
|
+
children: [
|
|
6242
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", style.dot), "aria-hidden": "true" }),
|
|
6243
|
+
displayLabel
|
|
6244
|
+
]
|
|
6245
|
+
}
|
|
6246
|
+
);
|
|
6247
|
+
});
|
|
6248
|
+
StatusBadge.displayName = "StatusBadge";
|
|
6249
|
+
var KIND_ORDER = ["overdue", "risk", "dueSoon", "stale"];
|
|
6250
|
+
var KIND_TONES = {
|
|
6251
|
+
overdue: "negative",
|
|
6252
|
+
risk: "negative",
|
|
6253
|
+
dueSoon: "warning",
|
|
6254
|
+
stale: "info"
|
|
6255
|
+
};
|
|
6256
|
+
var daysEn = (n) => n === 1 ? "1 day" : `${n} days`;
|
|
6257
|
+
var TEXT17 = {
|
|
6258
|
+
ja: {
|
|
6259
|
+
labels: { overdue: "\u671F\u65E5\u8D85\u904E", risk: "\u30EA\u30B9\u30AF", dueSoon: "\u671F\u9650\u9593\u8FD1", stale: "\u6EDE\u7559" },
|
|
6260
|
+
overdue: (days) => `\u671F\u65E5\u3092 ${days} \u65E5\u904E\u304E\u3066\u3044\u307E\u3059`,
|
|
6261
|
+
dueToday: "\u671F\u65E5\u306F\u4ECA\u65E5\u3067\u3059",
|
|
6262
|
+
dueIn: (days) => `\u671F\u65E5\u307E\u3067\u3042\u3068 ${days} \u65E5\u3067\u3059`,
|
|
6263
|
+
stale: (days) => `${days} \u65E5\u9593\u52D5\u304D\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
6264
|
+
empty: "\u5BFE\u5FDC\u304C\u5FC5\u8981\u306A\u9805\u76EE\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
6265
|
+
due: "\u671F\u65E5",
|
|
6266
|
+
nextAction: "\u6B21\u306E\u4E00\u624B: "
|
|
6267
|
+
},
|
|
6268
|
+
en: {
|
|
6269
|
+
labels: { overdue: "Overdue", risk: "At risk", dueSoon: "Due soon", stale: "Stalled" },
|
|
6270
|
+
overdue: (days) => `${daysEn(days)} overdue`,
|
|
6271
|
+
dueToday: "Due today",
|
|
6272
|
+
dueIn: (days) => `Due in ${daysEn(days)}`,
|
|
6273
|
+
stale: (days) => `No activity for ${daysEn(days)}`,
|
|
6274
|
+
empty: "Nothing needs attention",
|
|
6275
|
+
due: "Due",
|
|
6276
|
+
nextAction: "Next step: "
|
|
6277
|
+
}
|
|
6278
|
+
};
|
|
6279
|
+
TEXT17.en.labels;
|
|
6280
|
+
function toDate2(value) {
|
|
6281
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
6282
|
+
const date = typeof value === "string" ? parseISO(value) : value;
|
|
6283
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
6284
|
+
}
|
|
6285
|
+
function judgeAttention(input) {
|
|
6286
|
+
const asOf = toDate2(input.asOf);
|
|
6287
|
+
if (!asOf) return null;
|
|
6288
|
+
const dueSoonDays = input.dueSoonDays ?? 7;
|
|
6289
|
+
const due = toDate2(input.dueDate);
|
|
6290
|
+
if (due) {
|
|
6291
|
+
const untilDue = differenceInCalendarDays(due, asOf);
|
|
6292
|
+
if (untilDue < 0) return { kind: "overdue", days: -untilDue };
|
|
6293
|
+
}
|
|
6294
|
+
if (input.risk) return { kind: "risk", days: 0 };
|
|
6295
|
+
if (due) {
|
|
6296
|
+
const untilDue = differenceInCalendarDays(due, asOf);
|
|
6297
|
+
if (untilDue <= dueSoonDays) return { kind: "dueSoon", days: untilDue };
|
|
6298
|
+
}
|
|
6299
|
+
const last = toDate2(input.lastActivityAt);
|
|
6300
|
+
if (last && input.staleDays !== void 0) {
|
|
6301
|
+
const idle = differenceInCalendarDays(asOf, last);
|
|
6302
|
+
if (idle >= input.staleDays) return { kind: "stale", days: idle };
|
|
6303
|
+
}
|
|
6304
|
+
return null;
|
|
6305
|
+
}
|
|
6306
|
+
function compareAttention(a, b) {
|
|
6307
|
+
if (!a || !b) return a ? -1 : b ? 1 : 0;
|
|
6308
|
+
const byKind = KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind);
|
|
6309
|
+
if (byKind !== 0) return byKind;
|
|
6310
|
+
return a.kind === "dueSoon" ? a.days - b.days : b.days - a.days;
|
|
6311
|
+
}
|
|
6312
|
+
function defaultReason(j, labels, t) {
|
|
6313
|
+
switch (j.kind) {
|
|
6314
|
+
case "overdue":
|
|
6315
|
+
return t.overdue(j.days);
|
|
6316
|
+
case "dueSoon":
|
|
6317
|
+
return j.days === 0 ? t.dueToday : t.dueIn(j.days);
|
|
6318
|
+
case "stale":
|
|
6319
|
+
return t.stale(j.days);
|
|
6320
|
+
case "risk":
|
|
6321
|
+
return labels.risk;
|
|
6322
|
+
}
|
|
6323
|
+
}
|
|
6324
|
+
function cell(row, key) {
|
|
6325
|
+
if (!key || row === null || typeof row !== "object") return void 0;
|
|
6326
|
+
return row[key];
|
|
6327
|
+
}
|
|
6328
|
+
function asDateInput(value) {
|
|
6329
|
+
return typeof value === "string" || value instanceof Date ? value : void 0;
|
|
6330
|
+
}
|
|
6331
|
+
function asNode(value) {
|
|
6332
|
+
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
6333
|
+
}
|
|
6334
|
+
function AttentionList({
|
|
6335
|
+
rows,
|
|
6336
|
+
asOf,
|
|
6337
|
+
dueSoonDays,
|
|
6338
|
+
staleDays,
|
|
6339
|
+
labels,
|
|
6340
|
+
getRowId,
|
|
6341
|
+
getTitle,
|
|
6342
|
+
getDueDate,
|
|
6343
|
+
getLastActivityAt,
|
|
6344
|
+
isRisk,
|
|
6345
|
+
idKey,
|
|
6346
|
+
titleKey,
|
|
6347
|
+
dueDateKey,
|
|
6348
|
+
lastActivityAtKey,
|
|
6349
|
+
riskKey,
|
|
6350
|
+
nextActionKey,
|
|
6351
|
+
getReason,
|
|
6352
|
+
getNextAction,
|
|
6353
|
+
getMeta,
|
|
6354
|
+
onRowClick,
|
|
6355
|
+
includeUnflagged = false,
|
|
6356
|
+
emptyTitle,
|
|
6357
|
+
emptyDescription,
|
|
6358
|
+
className,
|
|
6359
|
+
...rest
|
|
6360
|
+
}) {
|
|
6361
|
+
const locale = useLocale();
|
|
6362
|
+
const t = TEXT17[uiLanguage(locale)];
|
|
6363
|
+
const mergedLabels = useMemo(() => ({ ...t.labels, ...labels }), [t, labels]);
|
|
6364
|
+
const colorMap = KIND_TONES;
|
|
6365
|
+
const dueOf = (row) => getDueDate ? getDueDate(row) : asDateInput(cell(row, dueDateKey));
|
|
6366
|
+
const nextActionOf = (row, j) => getNextAction ? getNextAction(row, j) : asNode(cell(row, nextActionKey));
|
|
6367
|
+
const items = (rows ?? []).map((row, index) => ({
|
|
6368
|
+
row,
|
|
6369
|
+
index,
|
|
6370
|
+
judgement: judgeAttention({
|
|
6371
|
+
dueDate: dueOf(row),
|
|
6372
|
+
lastActivityAt: getLastActivityAt ? getLastActivityAt(row) : asDateInput(cell(row, lastActivityAtKey)),
|
|
6373
|
+
risk: isRisk ? isRisk(row) : Boolean(cell(row, riskKey)),
|
|
6374
|
+
asOf,
|
|
6375
|
+
dueSoonDays,
|
|
6376
|
+
staleDays
|
|
6377
|
+
})
|
|
6378
|
+
})).filter((item) => includeUnflagged || item.judgement).sort((a, b) => compareAttention(a.judgement, b.judgement) || a.index - b.index);
|
|
6379
|
+
if (items.length === 0) {
|
|
6380
|
+
return /* @__PURE__ */ jsx(Empty, { title: emptyTitle ?? t.empty, description: emptyDescription, className });
|
|
6381
|
+
}
|
|
6382
|
+
return /* @__PURE__ */ jsx("ul", { "data-slot": "attention-list", className: cn("divide-y divide-border", className), ...rest, children: items.map(({ row, index, judgement }) => {
|
|
6383
|
+
const due = dueOf(row);
|
|
6384
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6385
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1", children: [
|
|
6386
|
+
judgement && /* @__PURE__ */ jsx(StatusBadge, { status: judgement.kind, label: mergedLabels[judgement.kind], colorMap }),
|
|
6387
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 font-medium", children: getTitle ? getTitle(row) : asNode(cell(row, titleKey)) }),
|
|
6388
|
+
getMeta && /* @__PURE__ */ jsx("span", { className: "text-sm tabular-nums text-muted-foreground", children: getMeta(row) }),
|
|
6389
|
+
due ? /* @__PURE__ */ jsxs("span", { className: "ml-auto text-sm tabular-nums text-muted-foreground", children: [
|
|
6390
|
+
t.due,
|
|
6391
|
+
" ",
|
|
6392
|
+
formatDate(due, "date", locale)
|
|
6393
|
+
] }) : null
|
|
6394
|
+
] }),
|
|
6395
|
+
judgement && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: getReason ? getReason(row, judgement) : defaultReason(judgement, mergedLabels, t) }),
|
|
6396
|
+
judgement && (getNextAction || nextActionKey) && nextActionOf(row, judgement) ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm", children: [
|
|
6397
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.nextAction }),
|
|
6398
|
+
nextActionOf(row, judgement)
|
|
6399
|
+
] }) : null
|
|
6400
|
+
] });
|
|
6401
|
+
return /* @__PURE__ */ jsx("li", { "data-slot": "attention-list-row", children: onRowClick ? /* @__PURE__ */ jsx(
|
|
6402
|
+
"button",
|
|
6403
|
+
{
|
|
6404
|
+
type: "button",
|
|
6405
|
+
onClick: () => onRowClick(row),
|
|
6406
|
+
className: "block w-full px-3 py-3 text-left transition-colors hover:bg-muted/50 focus-visible:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
6407
|
+
children: body
|
|
6408
|
+
}
|
|
6409
|
+
) : /* @__PURE__ */ jsx("div", { className: "px-3 py-3", children: body }) }, getRowId ? getRowId(row) : String(cell(row, idKey) ?? index));
|
|
6410
|
+
}) });
|
|
6411
|
+
}
|
|
6412
|
+
AttentionList.displayName = "AttentionList";
|
|
6413
|
+
function niceStep2(raw) {
|
|
6414
|
+
if (!(raw > 0) || !Number.isFinite(raw)) return 1;
|
|
6415
|
+
const exp10 = 10 ** Math.floor(Math.log10(raw));
|
|
6416
|
+
const f = raw / exp10;
|
|
6417
|
+
return (f < 1.5 ? 1 : f < 2.5 ? 2 : f < 4 ? 3 : f < 7 ? 5 : 10) * exp10;
|
|
6418
|
+
}
|
|
6419
|
+
var round = (n) => Number(n.toPrecision(12));
|
|
6420
|
+
function comboAxisScale(lo, hi, splitNumber, ticks) {
|
|
6421
|
+
let low = Math.min(0, Number.isFinite(lo) ? lo : 0);
|
|
6422
|
+
let high = Math.max(0, Number.isFinite(hi) ? hi : 0);
|
|
6423
|
+
if (high <= low) high = low + 1;
|
|
6424
|
+
let step = niceStep2((high - low) / Math.max(1, splitNumber));
|
|
6425
|
+
for (let attempt = 0; ; attempt++) {
|
|
6426
|
+
const min = round(Math.floor(low / step) * step);
|
|
6427
|
+
const max = round(Math.ceil(high / step) * step);
|
|
6428
|
+
const tick = ticks(Math.max(Math.abs(min), Math.abs(max)));
|
|
6429
|
+
const count = Math.round((max - min) / step);
|
|
6430
|
+
const labels = Array.from({ length: count + 1 }, (_, i) => tick(round(min + i * step)));
|
|
6431
|
+
if (new Set(labels).size === labels.length || attempt >= 6) return { min, max, interval: step, tick };
|
|
6432
|
+
step = niceStep2(step * 2);
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
var DEFAULT_CHART_HEIGHT2 = 288;
|
|
6436
|
+
var splitCountFor = (height) => height < 200 ? 3 : height < 300 ? 4 : 5;
|
|
6437
|
+
function escapeHtml3(text) {
|
|
6438
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
6439
|
+
}
|
|
6440
|
+
var TEXT18 = {
|
|
6441
|
+
ja: { item: "\u9805\u76EE", chart: "\u8907\u5408\u30B0\u30E9\u30D5" },
|
|
6442
|
+
en: { item: "Item", chart: "Combination chart" }
|
|
6443
|
+
};
|
|
6444
|
+
var textOf6 = (locale) => TEXT18[uiLanguage(resolveLocale(locale))];
|
|
6445
|
+
var axisOf = (s) => s.axis === "right" ? "right" : "left";
|
|
6446
|
+
function comboSeriesList(series) {
|
|
6447
|
+
if (!Array.isArray(series)) return [];
|
|
6448
|
+
return series.filter(
|
|
6449
|
+
(s) => !!s && typeof s === "object" && typeof s.key === "string" && ["bar", "line", "area"].includes(s.type)
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
function buildComboChartOption(config) {
|
|
6453
|
+
const { data, index, labels, formatters } = config;
|
|
6454
|
+
const series = comboSeriesList(config.series);
|
|
6455
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
6456
|
+
const seriesName = (s) => s.label ?? label2(s.key);
|
|
6457
|
+
const vertical = config.layout === "vertical";
|
|
6458
|
+
const categories = data.map((row) => String(row?.[index] ?? ""));
|
|
6459
|
+
const values = series.map((s) => data.map((row) => toChartNumber(row?.[s.key])));
|
|
6460
|
+
const hasRight = series.some((s) => axisOf(s) === "right");
|
|
6461
|
+
const axes = hasRight ? ["left", "right"] : ["left"];
|
|
6462
|
+
const extent = (axis) => {
|
|
6463
|
+
let [lo, hi] = [0, 0];
|
|
6464
|
+
const add = (v) => {
|
|
6465
|
+
if (v !== null && Number.isFinite(v)) [lo, hi] = [Math.min(lo, v), Math.max(hi, v)];
|
|
6466
|
+
};
|
|
6467
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
6468
|
+
series.forEach((s, i) => {
|
|
6469
|
+
if (axisOf(s) !== axis) return;
|
|
6470
|
+
if (config.stacked && s.type !== "line") {
|
|
6471
|
+
const stack = stacks.get(s.type) ?? { pos: data.map(() => 0), neg: data.map(() => 0) };
|
|
6472
|
+
values[i].forEach((v, j) => {
|
|
6473
|
+
if (v === null) return;
|
|
6474
|
+
if (v >= 0) stack.pos[j] += v;
|
|
6475
|
+
else stack.neg[j] += v;
|
|
6476
|
+
});
|
|
6477
|
+
stacks.set(s.type, stack);
|
|
6478
|
+
} else values[i].forEach(add);
|
|
6479
|
+
});
|
|
6480
|
+
for (const stack of stacks.values()) [...stack.pos, ...stack.neg].forEach(add);
|
|
6481
|
+
for (const ref of config.referenceLines ?? []) if (axisOf(ref) === axis) add(toChartNumber(ref.value));
|
|
6482
|
+
return [lo, hi];
|
|
6483
|
+
};
|
|
6484
|
+
const split = splitCountFor(config.plotHeight ?? DEFAULT_CHART_HEIGHT2);
|
|
6485
|
+
const scales = Object.fromEntries(axes.map((axis) => [axis, comboAxisScale(...extent(axis), split, config.ticks[axis])]));
|
|
6486
|
+
const valueAxes = axes.map((axis) => ({
|
|
6487
|
+
type: "value",
|
|
6488
|
+
position: vertical ? axis === "left" ? "bottom" : "top" : axis,
|
|
6489
|
+
min: scales[axis].min,
|
|
6490
|
+
max: scales[axis].max,
|
|
6491
|
+
interval: scales[axis].interval,
|
|
6492
|
+
axisLine: { show: false },
|
|
6493
|
+
axisTick: { show: false },
|
|
6494
|
+
splitLine: { show: axis === "left" },
|
|
6495
|
+
axisLabel: { formatter: (v) => scales[axis].tick(v), hideOverlap: vertical ? true : void 0 }
|
|
6496
|
+
}));
|
|
6497
|
+
const categoryAxis = {
|
|
6498
|
+
type: "category",
|
|
6499
|
+
data: categories,
|
|
6500
|
+
inverse: vertical,
|
|
6501
|
+
axisTick: { show: false },
|
|
6502
|
+
axisLabel: { formatter: (v) => label2(v), hideOverlap: true }
|
|
6503
|
+
};
|
|
6504
|
+
const axisIndex = (s) => axisOf(s) === "right" ? 1 : 0;
|
|
6505
|
+
const drawn = series.map((s, i) => {
|
|
6506
|
+
const color = config.palette[i] || void 0;
|
|
6507
|
+
const common = {
|
|
6508
|
+
id: `series-${i}`,
|
|
6509
|
+
name: seriesName(s),
|
|
6510
|
+
data: values[i],
|
|
6511
|
+
[vertical ? "xAxisIndex" : "yAxisIndex"]: axisIndex(s),
|
|
6512
|
+
emphasis: { focus: "none" }
|
|
6513
|
+
};
|
|
6514
|
+
if (s.type === "bar")
|
|
6515
|
+
return {
|
|
6516
|
+
...common,
|
|
6517
|
+
type: "bar",
|
|
6518
|
+
stack: config.stacked ? `bar-${axisOf(s)}` : void 0,
|
|
6519
|
+
barMaxWidth: 40,
|
|
6520
|
+
itemStyle: { color, borderRadius: vertical ? [0, 2, 2, 0] : [2, 2, 0, 0] }
|
|
6521
|
+
};
|
|
6522
|
+
return {
|
|
6523
|
+
...common,
|
|
6524
|
+
type: "line",
|
|
6525
|
+
stack: config.stacked && s.type === "area" ? `area-${axisOf(s)}` : void 0,
|
|
6526
|
+
symbol: "circle",
|
|
6527
|
+
symbolSize: 6,
|
|
6528
|
+
showSymbol: data.length <= 24,
|
|
6529
|
+
connectNulls: false,
|
|
6530
|
+
lineStyle: { width: 2, color },
|
|
6531
|
+
itemStyle: { color },
|
|
6532
|
+
areaStyle: s.type === "area" ? { color, opacity: 0.18 } : void 0,
|
|
6533
|
+
z: 3
|
|
6534
|
+
};
|
|
6535
|
+
});
|
|
6536
|
+
const references = axes.flatMap((axis) => {
|
|
6537
|
+
const lines = (config.referenceLines ?? []).filter((r) => axisOf(r) === axis && toChartNumber(r.value) !== null);
|
|
6538
|
+
if (lines.length === 0) return [];
|
|
6539
|
+
return [
|
|
6540
|
+
{
|
|
6541
|
+
id: `reference-${axis}`,
|
|
6542
|
+
type: "line",
|
|
6543
|
+
data: data.map(() => null),
|
|
6544
|
+
[vertical ? "xAxisIndex" : "yAxisIndex"]: axis === "right" ? 1 : 0,
|
|
6545
|
+
silent: true,
|
|
6546
|
+
symbol: "none",
|
|
6547
|
+
tooltip: { show: false },
|
|
6548
|
+
markLine: {
|
|
6549
|
+
silent: true,
|
|
6550
|
+
symbol: "none",
|
|
6551
|
+
lineStyle: { type: "dashed", color: config.referenceColor || void 0, width: 1 },
|
|
6552
|
+
label: { position: "insideEndTop", color: config.referenceColor || void 0 },
|
|
6553
|
+
data: lines.map((r) => ({
|
|
6554
|
+
[vertical ? "xAxis" : "yAxis"]: toChartNumber(r.value),
|
|
6555
|
+
label: { formatter: r.label ?? config.formatters[axis](toChartNumber(r.value)) }
|
|
6556
|
+
}))
|
|
6557
|
+
}
|
|
6558
|
+
}
|
|
6559
|
+
];
|
|
6560
|
+
});
|
|
6561
|
+
const showLegend = (config.showLegend ?? true) && series.length > 1;
|
|
6562
|
+
const tooltip = (params) => {
|
|
6563
|
+
const items = Array.isArray(params) ? params : [params];
|
|
6564
|
+
const first = items[0];
|
|
6565
|
+
if (!first) return "";
|
|
6566
|
+
const head = escapeHtml3(label2(categories[first.dataIndex] ?? ""));
|
|
6567
|
+
const lines = items.filter((p) => p.seriesIndex < series.length).map((p) => {
|
|
6568
|
+
const s = series[p.seriesIndex];
|
|
6569
|
+
const v = values[p.seriesIndex][p.dataIndex];
|
|
6570
|
+
return `${p.marker ?? ""}${escapeHtml3(seriesName(s))} ${v === null || v === void 0 ? EMPTY_VALUE : escapeHtml3(formatters[axisOf(s)](v))}`;
|
|
6571
|
+
});
|
|
6572
|
+
return [head, ...lines].join("<br/>");
|
|
6573
|
+
};
|
|
6574
|
+
const option = {
|
|
6575
|
+
animation: false,
|
|
6576
|
+
legend: { show: showLegend, top: 0, left: 0, data: series.map(seriesName) },
|
|
6577
|
+
tooltip: config.showTooltip === false ? { show: false } : { trigger: "axis", axisPointer: { type: "shadow" }, formatter: tooltip },
|
|
6578
|
+
grid: { left: 8, right: 8, top: showLegend ? 36 : 12, bottom: 8, containLabel: true },
|
|
6579
|
+
xAxis: vertical ? valueAxes : categoryAxis,
|
|
6580
|
+
yAxis: vertical ? categoryAxis : valueAxes,
|
|
6581
|
+
series: [...drawn, ...references]
|
|
6582
|
+
};
|
|
6583
|
+
return config.chartOptions ? mergeDeep2(option, config.chartOptions) : option;
|
|
6584
|
+
}
|
|
6585
|
+
function mergeDeep2(a, b) {
|
|
6586
|
+
const out = { ...a };
|
|
6587
|
+
for (const [k, v] of Object.entries(b)) {
|
|
6588
|
+
const cur = out[k];
|
|
6589
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep2(cur, v) : v;
|
|
6590
|
+
}
|
|
6591
|
+
return out;
|
|
6592
|
+
}
|
|
6593
|
+
function comboChartDataTable(data, index, series, formatters, labels, locale) {
|
|
6594
|
+
const list = comboSeriesList(series);
|
|
6595
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
6596
|
+
return {
|
|
6597
|
+
columns: [labels?.[index] ?? textOf6(locale).item, ...list.map((s) => s.label ?? label2(s.key))],
|
|
6598
|
+
rows: data.map((row) => [
|
|
6599
|
+
label2(String(row?.[index] ?? "")),
|
|
6600
|
+
...list.map((s) => {
|
|
6601
|
+
const n = toChartNumber(row?.[s.key]);
|
|
6602
|
+
return n === null ? EMPTY_VALUE : formatters[axisOf(s)](n);
|
|
6603
|
+
})
|
|
6604
|
+
])
|
|
6605
|
+
};
|
|
6606
|
+
}
|
|
6607
|
+
var NO_ROWS13 = [];
|
|
6608
|
+
function ComboChartImpl({
|
|
6609
|
+
data = NO_ROWS13,
|
|
6610
|
+
index,
|
|
6611
|
+
series = NO_ROWS13,
|
|
6612
|
+
format,
|
|
6613
|
+
rightFormat,
|
|
6614
|
+
labels,
|
|
6615
|
+
referenceLines,
|
|
6616
|
+
stacked = false,
|
|
6617
|
+
layout = "horizontal",
|
|
6618
|
+
showLegend = true,
|
|
6619
|
+
showTooltip = true,
|
|
6620
|
+
onSelect,
|
|
6621
|
+
title,
|
|
6622
|
+
summary,
|
|
6623
|
+
isLoading,
|
|
6624
|
+
emptyState,
|
|
6625
|
+
height,
|
|
6626
|
+
chartOptions,
|
|
6627
|
+
actions,
|
|
6628
|
+
fileName,
|
|
6629
|
+
className,
|
|
6630
|
+
ariaLabel,
|
|
6631
|
+
...rest
|
|
6632
|
+
}, ref) {
|
|
6633
|
+
const rows = Array.isArray(data) ? data : NO_ROWS13;
|
|
6634
|
+
const list = useMemo(() => comboSeriesList(series), [series]);
|
|
6635
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
6636
|
+
const locale = useLocale();
|
|
6637
|
+
const formatters = useMemo(
|
|
6638
|
+
() => ({ left: resolveValueFormatter(void 0, format, locale), right: resolveValueFormatter(void 0, rightFormat ?? format, locale) }),
|
|
6639
|
+
[format, rightFormat, locale]
|
|
6640
|
+
);
|
|
6641
|
+
const ticks = useMemo(
|
|
6642
|
+
() => ({ left: resolveAxisTickFormatter(void 0, format, locale), right: resolveAxisTickFormatter(void 0, rightFormat ?? format, locale) }),
|
|
6643
|
+
[format, rightFormat, locale]
|
|
6644
|
+
);
|
|
6645
|
+
const palette = useMemo(() => resolveColors(tokens, list), [tokens, list]);
|
|
6646
|
+
const option = useMemo(
|
|
6647
|
+
() => buildComboChartOption({
|
|
6648
|
+
data: rows,
|
|
6649
|
+
index,
|
|
6650
|
+
series: list,
|
|
6651
|
+
palette: palette.series,
|
|
6652
|
+
referenceColor: palette.reference,
|
|
6653
|
+
labels,
|
|
6654
|
+
formatters,
|
|
6655
|
+
ticks,
|
|
6656
|
+
referenceLines,
|
|
6657
|
+
stacked,
|
|
6658
|
+
layout,
|
|
6659
|
+
showLegend,
|
|
6660
|
+
showTooltip,
|
|
6661
|
+
plotHeight: height,
|
|
6662
|
+
chartOptions
|
|
6663
|
+
}),
|
|
6664
|
+
[rows, index, list, palette, labels, formatters, ticks, referenceLines, stacked, layout, showLegend, showTooltip, height, chartOptions]
|
|
6665
|
+
);
|
|
6666
|
+
const onEvents = useMemo(
|
|
6667
|
+
() => onSelect ? {
|
|
6668
|
+
click: (params) => {
|
|
6669
|
+
const p = params;
|
|
6670
|
+
if (p.componentType !== "series" || (p.seriesIndex ?? Infinity) >= list.length) return;
|
|
6671
|
+
const row = rows[p.dataIndex ?? -1];
|
|
6672
|
+
if (row) onSelect(row);
|
|
6673
|
+
}
|
|
6674
|
+
} : void 0,
|
|
6675
|
+
[onSelect, rows, list.length]
|
|
6676
|
+
);
|
|
6677
|
+
const dataTable = useMemo(() => comboChartDataTable(rows, index, list, formatters, labels, locale), [rows, index, list, formatters, labels, locale]);
|
|
6678
|
+
const label2 = ariaLabel ?? textOf6(locale).chart;
|
|
6679
|
+
return /* @__PURE__ */ jsx(
|
|
6680
|
+
ChartFrame,
|
|
6681
|
+
{
|
|
6682
|
+
...chartRootProps(rest),
|
|
6683
|
+
ref: frameRef,
|
|
6684
|
+
title,
|
|
6685
|
+
summary,
|
|
6686
|
+
isLoading: isLoading ?? false,
|
|
6687
|
+
isEmpty: rows.length === 0,
|
|
6688
|
+
emptyState,
|
|
6689
|
+
height,
|
|
6690
|
+
variant: "bar",
|
|
6691
|
+
ariaLabel: label2,
|
|
6692
|
+
className,
|
|
6693
|
+
dataTable,
|
|
6694
|
+
children: /* @__PURE__ */ jsx(EChart, { option, onEvents, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary } })
|
|
6695
|
+
}
|
|
6696
|
+
);
|
|
6697
|
+
}
|
|
6698
|
+
function resolveColors(tokens, series) {
|
|
6699
|
+
const resolved = resolveChartColors(
|
|
6700
|
+
["var(--muted-foreground)", ...series.map((s, i) => s.color ?? DEFAULT_CHART_COLORS[i % DEFAULT_CHART_COLORS.length])],
|
|
6701
|
+
tokens
|
|
6702
|
+
);
|
|
6703
|
+
return { reference: resolved[0] ?? "", series: resolved.slice(1) };
|
|
6704
|
+
}
|
|
6705
|
+
var ComboChart = forwardRef(ComboChartImpl);
|
|
6706
|
+
ComboChart.displayName = "ComboChart";
|
|
6707
|
+
|
|
6708
|
+
// ../squadkit/registry/lib/forecast.ts
|
|
6709
|
+
var FORECAST_MIN_POINTS = 5;
|
|
6710
|
+
var FORECAST_MAX_FIT_POINTS = 1e3;
|
|
6711
|
+
function inverseNormal(p) {
|
|
6712
|
+
if (!(p > 0 && p < 1)) return p <= 0 ? -Infinity : Infinity;
|
|
6713
|
+
const a = [-39.69683028665376, 220.9460984245205, -275.9285104469687, 138.357751867269, -30.66479806614716, 2.506628277459239];
|
|
6714
|
+
const b = [-54.47609879822406, 161.5858368580409, -155.6989798598866, 66.80131188771972, -13.28068155288572];
|
|
6715
|
+
const c = [-0.007784894002430293, -0.3223964580411365, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783];
|
|
6716
|
+
const d = [0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416];
|
|
6717
|
+
const low = 0.02425;
|
|
6718
|
+
if (p < low) {
|
|
6719
|
+
const q2 = Math.sqrt(-2 * Math.log(p));
|
|
6720
|
+
return (((((c[0] * q2 + c[1]) * q2 + c[2]) * q2 + c[3]) * q2 + c[4]) * q2 + c[5]) / ((((d[0] * q2 + d[1]) * q2 + d[2]) * q2 + d[3]) * q2 + 1);
|
|
6721
|
+
}
|
|
6722
|
+
if (p > 1 - low) {
|
|
6723
|
+
const q2 = Math.sqrt(-2 * Math.log(1 - p));
|
|
6724
|
+
return -(((((c[0] * q2 + c[1]) * q2 + c[2]) * q2 + c[3]) * q2 + c[4]) * q2 + c[5]) / ((((d[0] * q2 + d[1]) * q2 + d[2]) * q2 + d[3]) * q2 + 1);
|
|
6725
|
+
}
|
|
6726
|
+
const q = p - 0.5;
|
|
6727
|
+
const r = q * q;
|
|
6728
|
+
return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q / (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
|
|
6729
|
+
}
|
|
6730
|
+
function nelderMead(f, start, step, maxEvals) {
|
|
6731
|
+
const dim = start.length;
|
|
6732
|
+
const simplex = [{ x: start.slice(), fx: f(start) }];
|
|
6733
|
+
for (let i = 0; i < dim; i++) {
|
|
6734
|
+
const x = start.slice();
|
|
6735
|
+
x[i] = x[i] + step;
|
|
6736
|
+
simplex.push({ x, fx: f(x) });
|
|
6737
|
+
}
|
|
6738
|
+
let evals = dim + 1;
|
|
6739
|
+
const combine = (a, b, t) => a.map((v, i) => v + t * (b[i] - v));
|
|
6740
|
+
while (evals < maxEvals) {
|
|
6741
|
+
simplex.sort((p, q) => p.fx - q.fx);
|
|
6742
|
+
const best = simplex[0];
|
|
6743
|
+
const worst = simplex[dim];
|
|
6744
|
+
const spread = Math.abs(worst.fx - best.fx);
|
|
6745
|
+
if (spread <= 1e-10 * (Math.abs(best.fx) + 1e-20)) break;
|
|
6746
|
+
const centroid = new Array(dim).fill(0);
|
|
6747
|
+
for (let i = 0; i < dim; i++) for (let j = 0; j < dim; j++) centroid[j] += simplex[i].x[j] / dim;
|
|
6748
|
+
const reflected = combine(centroid, worst.x, -1);
|
|
6749
|
+
const fr = f(reflected);
|
|
6750
|
+
evals++;
|
|
6751
|
+
if (fr < best.fx) {
|
|
6752
|
+
const expanded = combine(centroid, worst.x, -2);
|
|
6753
|
+
const fe = f(expanded);
|
|
6754
|
+
evals++;
|
|
6755
|
+
simplex[dim] = fe < fr ? { x: expanded, fx: fe } : { x: reflected, fx: fr };
|
|
6756
|
+
continue;
|
|
6757
|
+
}
|
|
6758
|
+
if (fr < simplex[dim - 1].fx) {
|
|
6759
|
+
simplex[dim] = { x: reflected, fx: fr };
|
|
6760
|
+
continue;
|
|
6761
|
+
}
|
|
6762
|
+
const outside = fr < worst.fx;
|
|
6763
|
+
const contracted = outside ? combine(centroid, reflected, 0.5) : combine(centroid, worst.x, 0.5);
|
|
6764
|
+
const fc = f(contracted);
|
|
6765
|
+
evals++;
|
|
6766
|
+
if (fc < (outside ? fr : worst.fx)) {
|
|
6767
|
+
simplex[dim] = { x: contracted, fx: fc };
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6770
|
+
for (let i = 1; i <= dim; i++) {
|
|
6771
|
+
const x = combine(best.x, simplex[i].x, 0.5);
|
|
6772
|
+
simplex[i] = { x, fx: f(x) };
|
|
6773
|
+
evals++;
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
simplex.sort((p, q) => p.fx - q.fx);
|
|
6777
|
+
return simplex[0];
|
|
6778
|
+
}
|
|
6779
|
+
function run(y, spec, p) {
|
|
6780
|
+
let l = p.l0;
|
|
6781
|
+
let b = spec.trend === "additive" ? p.b0 : 0;
|
|
6782
|
+
const s = spec.season === "none" ? [] : p.s0.slice();
|
|
6783
|
+
const mult = spec.season === "multiplicative";
|
|
6784
|
+
let sse = 0;
|
|
6785
|
+
for (let t = 0; t < y.length; t++) {
|
|
6786
|
+
const j = t % spec.m;
|
|
6787
|
+
const base = l + b;
|
|
6788
|
+
const sj = spec.season === "none" ? mult ? 1 : 0 : s[j];
|
|
6789
|
+
if (mult && (sj <= 0 || base <= 0)) return null;
|
|
6790
|
+
const yhat = mult ? base * sj : base + sj;
|
|
6791
|
+
const e = y[t] - yhat;
|
|
6792
|
+
if (!Number.isFinite(e)) return null;
|
|
6793
|
+
sse += e * e;
|
|
6794
|
+
if (mult) {
|
|
6795
|
+
l = base + p.alpha * e / sj;
|
|
6796
|
+
if (spec.trend === "additive") b = b + p.beta * e / sj;
|
|
6797
|
+
s[j] = sj + p.gamma * e / base;
|
|
6798
|
+
} else {
|
|
6799
|
+
l = base + p.alpha * e;
|
|
6800
|
+
if (spec.trend === "additive") b = b + p.beta * e;
|
|
6801
|
+
if (spec.season === "additive") s[j] = sj + p.gamma * e;
|
|
6802
|
+
}
|
|
6803
|
+
}
|
|
6804
|
+
return Number.isFinite(sse) ? { sse, level: l, slope: b, season: s } : null;
|
|
6805
|
+
}
|
|
6806
|
+
function initialSeason(y, m, mult) {
|
|
6807
|
+
const len = Math.min(Math.floor(y.length / m), 3) * m;
|
|
6808
|
+
const half = Math.floor(m / 2);
|
|
6809
|
+
const sums = new Array(m).fill(0);
|
|
6810
|
+
const counts = new Array(m).fill(0);
|
|
6811
|
+
for (let t = half; t < len - half; t++) {
|
|
6812
|
+
let trend;
|
|
6813
|
+
if (m % 2 === 0) {
|
|
6814
|
+
let sum = 0.5 * y[t - half] + 0.5 * y[t + half];
|
|
6815
|
+
for (let k = t - half + 1; k < t + half; k++) sum += y[k];
|
|
6816
|
+
trend = sum / m;
|
|
6817
|
+
} else {
|
|
6818
|
+
let sum = 0;
|
|
6819
|
+
for (let k = t - half; k <= t + half; k++) sum += y[k];
|
|
6820
|
+
trend = sum / m;
|
|
6821
|
+
}
|
|
6822
|
+
if (mult && trend <= 0) continue;
|
|
6823
|
+
sums[t % m] += mult ? y[t] / trend : y[t] - trend;
|
|
6824
|
+
counts[t % m]++;
|
|
6825
|
+
}
|
|
6826
|
+
const raw = sums.map((v, j) => counts[j] > 0 ? v / counts[j] : mult ? 1 : 0);
|
|
6827
|
+
const mean = raw.reduce((a, v) => a + v, 0) / m;
|
|
6828
|
+
return mult ? raw.map((v) => mean > 0 ? v / mean : 1) : raw.map((v) => v - mean);
|
|
6829
|
+
}
|
|
6830
|
+
function linearFit(z) {
|
|
6831
|
+
const n = z.length;
|
|
6832
|
+
if (n < 2) return { intercept: z[0] ?? 0, slope: 0 };
|
|
6833
|
+
const mt = (n - 1) / 2;
|
|
6834
|
+
const mz = z.reduce((a, v) => a + v, 0) / n;
|
|
6835
|
+
let num = 0;
|
|
6836
|
+
let den = 0;
|
|
6837
|
+
z.forEach((v, t) => {
|
|
6838
|
+
num += (t - mt) * (v - mz);
|
|
6839
|
+
den += (t - mt) * (t - mt);
|
|
6840
|
+
});
|
|
6841
|
+
const slope = den === 0 ? 0 : num / den;
|
|
6842
|
+
return { intercept: mz - slope * mt, slope };
|
|
6843
|
+
}
|
|
6844
|
+
var logistic = (x) => 1 / (1 + Math.exp(-x));
|
|
6845
|
+
var logit = (p) => Math.log(p / (1 - p));
|
|
6846
|
+
var EDGE = 1e-4;
|
|
6847
|
+
function fitModel(y, spec) {
|
|
6848
|
+
const n = y.length;
|
|
6849
|
+
const { m } = spec;
|
|
6850
|
+
const seasonal = spec.season !== "none";
|
|
6851
|
+
const mult = spec.season === "multiplicative";
|
|
6852
|
+
const s0 = seasonal ? initialSeason(y, m, mult) : [];
|
|
6853
|
+
const adjusted = y.map((v, t) => !seasonal ? v : mult ? v / s0[t % m] : v - s0[t % m]);
|
|
6854
|
+
const head = adjusted.slice(0, seasonal ? Math.min(n, Math.max(2 * m, 10)) : Math.min(n, 10));
|
|
6855
|
+
const line = linearFit(head);
|
|
6856
|
+
const trend = spec.trend === "additive";
|
|
6857
|
+
const l0Init = trend ? line.intercept - line.slope : head.slice(0, Math.min(head.length, 4)).reduce((a, v) => a + v, 0) / Math.min(head.length, 4);
|
|
6858
|
+
const b0Init = trend ? line.slope : 0;
|
|
6859
|
+
const mean = y.reduce((a, v) => a + v, 0) / n;
|
|
6860
|
+
const sd = Math.sqrt(y.reduce((a, v) => a + (v - mean) * (v - mean), 0) / n);
|
|
6861
|
+
const scale = Math.max(sd, Math.abs(mean) * 1e-3, 1e-9);
|
|
6862
|
+
const decode = (x) => {
|
|
6863
|
+
let i = 0;
|
|
6864
|
+
const alpha = EDGE + (1 - 2 * EDGE) * logistic(x[i++]);
|
|
6865
|
+
const beta = trend ? alpha * (EDGE + (1 - 2 * EDGE) * logistic(x[i++])) : 0;
|
|
6866
|
+
const gamma = seasonal ? (1 - alpha) * (EDGE + (1 - 2 * EDGE) * logistic(x[i++])) : 0;
|
|
6867
|
+
const l0 = l0Init + x[i++] * scale;
|
|
6868
|
+
const b0 = trend ? b0Init + x[i++] * scale * 0.1 : 0;
|
|
6869
|
+
return { alpha, beta, gamma, l0, b0, s0 };
|
|
6870
|
+
};
|
|
6871
|
+
const objective = (x) => run(y, spec, decode(x))?.sse ?? Infinity;
|
|
6872
|
+
const starts = [
|
|
6873
|
+
[0.2, 0.1, 0.1],
|
|
6874
|
+
[0.5, 0.3, 0.3],
|
|
6875
|
+
[0.05, 0.5, 0.05]
|
|
6876
|
+
];
|
|
6877
|
+
let best = null;
|
|
6878
|
+
for (const [a, b, g] of starts) {
|
|
6879
|
+
const x0 = [logit(a), ...trend ? [logit(b)] : [], ...seasonal ? [logit(g)] : [], 0, ...trend ? [0] : []];
|
|
6880
|
+
let found = nelderMead(objective, x0, 1, 300 * x0.length);
|
|
6881
|
+
found = nelderMead(objective, found.x, 0.25, 200 * x0.length);
|
|
6882
|
+
if (best === null || found.fx < best.fx) best = found;
|
|
6883
|
+
}
|
|
6884
|
+
if (best === null || !Number.isFinite(best.fx)) return null;
|
|
6885
|
+
const params = decode(best.x);
|
|
6886
|
+
const state = run(y, spec, params);
|
|
6887
|
+
if (!state) return null;
|
|
6888
|
+
const k = 2 + (trend ? 2 : 0) + (seasonal ? 1 + (m - 1) : 0);
|
|
6889
|
+
const sse = Math.max(state.sse, 1e-12 * Math.max(1, mean * mean) * n);
|
|
6890
|
+
const aic = n * Math.log(sse / n) + 2 * k;
|
|
6891
|
+
const aicc = n - k - 1 > 0 ? aic + 2 * k * (k + 1) / (n - k - 1) : Infinity;
|
|
6892
|
+
return { spec, params, state, k, aicc };
|
|
6893
|
+
}
|
|
6894
|
+
function varianceMultiplier(fit, h, seasonIndex) {
|
|
6895
|
+
const { alpha: a, beta: b, gamma: g } = fit.params;
|
|
6896
|
+
const { m } = fit.spec;
|
|
6897
|
+
const trend = fit.spec.trend === "additive";
|
|
6898
|
+
const seasonal = fit.spec.season !== "none";
|
|
6899
|
+
const k = Math.floor((h - 1) / m);
|
|
6900
|
+
let extra = trend ? (h - 1) * (a * a + a * b * h + b * b * h * (2 * h - 1) / 6) : (h - 1) * a * a;
|
|
6901
|
+
if (seasonal) extra += trend ? g * k * (2 * a + g + b * m * (k + 1)) : g * k * (2 * a + g);
|
|
6902
|
+
if (fit.spec.season === "multiplicative") extra *= seasonIndex * seasonIndex;
|
|
6903
|
+
return 1 + extra;
|
|
6904
|
+
}
|
|
6905
|
+
function forecast(values, options = {}) {
|
|
6906
|
+
const finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
6907
|
+
let first = 0;
|
|
6908
|
+
while (first < values.length && !finite(values[first])) first++;
|
|
6909
|
+
let last = values.length - 1;
|
|
6910
|
+
while (last >= first && !finite(values[last])) last--;
|
|
6911
|
+
if (last < first) return null;
|
|
6912
|
+
const ignore = Math.max(0, Math.floor(options.ignoreLast ?? 0));
|
|
6913
|
+
const end = last + 1 - ignore;
|
|
6914
|
+
let start = first;
|
|
6915
|
+
if (end - start > FORECAST_MAX_FIT_POINTS) start = end - FORECAST_MAX_FIT_POINTS;
|
|
6916
|
+
if (end - start < FORECAST_MIN_POINTS) return null;
|
|
6917
|
+
const y = [];
|
|
6918
|
+
for (let t = start; t < end; t++) {
|
|
6919
|
+
const v = values[t];
|
|
6920
|
+
if (finite(v)) {
|
|
6921
|
+
y.push(v);
|
|
6922
|
+
continue;
|
|
6923
|
+
}
|
|
6924
|
+
let lo = t - 1;
|
|
6925
|
+
while (lo >= start && !finite(values[lo])) lo--;
|
|
6926
|
+
let hi = t + 1;
|
|
6927
|
+
while (hi < end && !finite(values[hi])) hi++;
|
|
6928
|
+
const a = lo >= start ? values[lo] : void 0;
|
|
6929
|
+
const b = hi < end ? values[hi] : void 0;
|
|
6930
|
+
y.push(a !== void 0 && b !== void 0 ? a + (b - a) * (t - lo) / (hi - lo) : a ?? b ?? 0);
|
|
6931
|
+
}
|
|
6932
|
+
const n = y.length;
|
|
6933
|
+
const m = Math.max(1, Math.floor(options.seasonLength ?? 1));
|
|
6934
|
+
const seasonOption = options.season ?? "auto";
|
|
6935
|
+
const trendOption = options.trend ?? "auto";
|
|
6936
|
+
const canSeason = m > 1 && n >= 2 * m && seasonOption !== "none";
|
|
6937
|
+
const positive = y.every((v) => v > 0);
|
|
6938
|
+
const seasons = !canSeason ? ["none"] : seasonOption === "additive" ? ["additive"] : seasonOption === "multiplicative" ? [positive ? "multiplicative" : "additive"] : positive ? ["none", "additive", "multiplicative"] : ["none", "additive"];
|
|
6939
|
+
const trends = trendOption === "none" ? ["none"] : trendOption === "auto" ? ["none", "additive"] : ["additive"];
|
|
6940
|
+
let chosen = null;
|
|
6941
|
+
for (const trend of trends) {
|
|
6942
|
+
for (const season of seasons) {
|
|
6943
|
+
const fit = fitModel(y, { trend, season, m: season === "none" ? 1 : m });
|
|
6944
|
+
if (!fit) continue;
|
|
6945
|
+
if (chosen === null || fit.aicc < chosen.aicc || !Number.isFinite(chosen.aicc) && fit.k < chosen.k) chosen = fit;
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6948
|
+
if (!chosen) return null;
|
|
6949
|
+
const dof = n - (chosen.k - 1);
|
|
6950
|
+
const sigma = Math.sqrt(chosen.state.sse / (dof >= 1 ? dof : n));
|
|
6951
|
+
const confidence = options.confidence !== void 0 && options.confidence > 0 && options.confidence < 1 ? options.confidence : 0.95;
|
|
6952
|
+
const z = inverseNormal(1 - (1 - confidence) / 2);
|
|
6953
|
+
const horizon = options.horizon !== void 0 && Number.isFinite(options.horizon) ? Math.max(0, Math.floor(options.horizon)) : m > 1 ? m : Math.min(12, Math.max(1, Math.round(n / 4)));
|
|
6954
|
+
const steps = values.length - end + horizon;
|
|
6955
|
+
const { spec, state } = chosen;
|
|
6956
|
+
const points = [];
|
|
6957
|
+
for (let h = 1; h <= steps; h++) {
|
|
6958
|
+
const base = state.level + h * state.slope;
|
|
6959
|
+
const s = spec.season === "none" ? void 0 : state.season[(n - 1 + h) % spec.m];
|
|
6960
|
+
const value = s === void 0 ? base : spec.season === "multiplicative" ? base * s : base + s;
|
|
6961
|
+
const half = z * sigma * Math.sqrt(varianceMultiplier(chosen, h, s ?? 1));
|
|
6962
|
+
points.push({ value, lower: value - half, upper: value + half });
|
|
6963
|
+
}
|
|
6964
|
+
const { alpha, beta, gamma } = chosen.params;
|
|
6965
|
+
return {
|
|
6966
|
+
model: {
|
|
6967
|
+
trend: spec.trend,
|
|
6968
|
+
season: spec.season,
|
|
6969
|
+
alpha,
|
|
6970
|
+
...spec.trend === "additive" ? { beta } : {},
|
|
6971
|
+
...spec.season !== "none" ? { gamma, seasonLength: spec.m } : {}
|
|
6972
|
+
},
|
|
6973
|
+
fitted: n,
|
|
6974
|
+
end,
|
|
6975
|
+
sigma,
|
|
6976
|
+
points
|
|
6977
|
+
};
|
|
6978
|
+
}
|
|
6979
|
+
var TEXT19 = {
|
|
6980
|
+
ja: {
|
|
6981
|
+
forecast: "\u4E88\u6E2C",
|
|
6982
|
+
note: (source) => source ? `\u4E88\u6E2C\u306F\u6307\u6570\u5E73\u6ED1\u6CD5\u306B\u3088\u308B\u8FD1\u4F3C\u5024\u3067\u3059\u3002${source} \u306E\u4E88\u6E2C\u5024\u3068\u306F\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002` : "\u4E88\u6E2C\u306F\u6307\u6570\u5E73\u6ED1\u6CD5\u306B\u3088\u308B\u8FD1\u4F3C\u5024\u3067\u3059\u3002\u5143\u306E\u30C4\u30FC\u30EB\u306E\u4E88\u6E2C\u5024\u3068\u306F\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002",
|
|
6983
|
+
interval: (pct, lo, hi) => `${pct} \u4E88\u6E2C\u533A\u9593 ${lo}\u301C${hi}`,
|
|
6984
|
+
chart: (names) => `${names.join("\u30FB")}\u306E\u5B9F\u7E3E\u3068\u4E88\u6E2C\u306E\u6298\u308C\u7DDA\u30B0\u30E9\u30D5`,
|
|
6985
|
+
item: "\u671F\u9593"
|
|
6986
|
+
},
|
|
6987
|
+
en: {
|
|
6988
|
+
forecast: "Forecast",
|
|
6989
|
+
note: (source) => `Forecast is an approximation (exponential smoothing); values do not match ${source ? `${source}'s` : "the source tool's"} forecast.`,
|
|
6990
|
+
interval: (pct, lo, hi) => `${pct} interval ${lo} \u2013 ${hi}`,
|
|
6991
|
+
chart: (names) => `Line chart of actuals and forecast for ${names.join(", ")}`,
|
|
6992
|
+
item: "Period"
|
|
6993
|
+
}
|
|
6994
|
+
};
|
|
6995
|
+
var LEGEND_DASHED_ICON = "path://M0,0 L42,0 L42,24 L0,24 Z M58,0 L100,0 L100,24 L58,24 Z";
|
|
6996
|
+
var BOUNDARY_LABEL_ROOM = 18;
|
|
6997
|
+
var MAX_PERIODS = 5e3;
|
|
6998
|
+
var DAY_MS = 864e5;
|
|
6999
|
+
var NO_ROWS14 = [];
|
|
7000
|
+
var DATE_RE = /^(\d{4})(?:[-/](\d{1,2})(?:[-/](\d{1,2}))?)?(?:[T ].*)?$/;
|
|
7001
|
+
var QUARTER_RE = /^(\d{4})[-\s]?Q([1-4])$/i;
|
|
7002
|
+
function parseDate(value) {
|
|
7003
|
+
if (value instanceof Date) {
|
|
7004
|
+
return Number.isNaN(value.getTime()) ? null : { y: value.getFullYear(), m: value.getMonth() + 1, d: value.getDate(), precision: "day" };
|
|
7005
|
+
}
|
|
7006
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
7007
|
+
if (Number.isInteger(value) && value >= 1e3 && value <= 9999) return { y: value, m: 1, d: 1, precision: "year" };
|
|
7008
|
+
return parseDate(new Date(value));
|
|
7009
|
+
}
|
|
7010
|
+
if (typeof value !== "string") return null;
|
|
7011
|
+
const text = value.trim();
|
|
7012
|
+
const q = QUARTER_RE.exec(text);
|
|
7013
|
+
if (q) return { y: Number(q[1]), m: (Number(q[2]) - 1) * 3 + 1, d: 1, precision: "month" };
|
|
7014
|
+
const m = DATE_RE.exec(text);
|
|
7015
|
+
if (m) {
|
|
7016
|
+
const month = m[2] === void 0 ? 1 : Number(m[2]);
|
|
7017
|
+
const day = m[3] === void 0 ? 1 : Number(m[3]);
|
|
7018
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) return null;
|
|
7019
|
+
return { y: Number(m[1]), m: month, d: day, precision: m[3] !== void 0 ? "day" : m[2] !== void 0 ? "month" : "year" };
|
|
7020
|
+
}
|
|
7021
|
+
if (text === "") return null;
|
|
7022
|
+
const parsed = new Date(text);
|
|
7023
|
+
return Number.isNaN(parsed.getTime()) ? null : parseDate(parsed);
|
|
7024
|
+
}
|
|
7025
|
+
var dayOrdinal = (p) => Math.round(Date.UTC(p.y, p.m - 1, p.d) / DAY_MS);
|
|
7026
|
+
function ordinal(p, period) {
|
|
7027
|
+
switch (period) {
|
|
7028
|
+
case "year":
|
|
7029
|
+
return p.y;
|
|
7030
|
+
case "quarter":
|
|
7031
|
+
return p.y * 4 + Math.floor((p.m - 1) / 3);
|
|
7032
|
+
case "month":
|
|
7033
|
+
return p.y * 12 + (p.m - 1);
|
|
7034
|
+
default:
|
|
7035
|
+
return dayOrdinal(p);
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
7039
|
+
function ordinalLabel(o, period) {
|
|
7040
|
+
if (period === "year") return String(o);
|
|
7041
|
+
if (period === "quarter") return `${Math.floor(o / 4)}-${pad2(o % 4 * 3 + 1)}`;
|
|
7042
|
+
if (period === "month") return `${Math.floor(o / 12)}-${pad2(o % 12 + 1)}`;
|
|
7043
|
+
const d = new Date(o * DAY_MS);
|
|
7044
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
|
|
7045
|
+
}
|
|
7046
|
+
function inferPeriod(dates) {
|
|
7047
|
+
if (dates.every((d) => d.precision === "year")) return "year";
|
|
7048
|
+
const coarse = dates.every((d) => d.precision !== "day");
|
|
7049
|
+
const keys = [...new Set(dates.map((d) => coarse ? ordinal(d, "month") : dayOrdinal(d)))].sort((a, b) => a - b);
|
|
7050
|
+
const gaps = keys.slice(1).map((k, i) => k - keys[i]).sort((a, b) => a - b);
|
|
7051
|
+
const gap = gaps.length === 0 ? coarse ? 1 : 1 : gaps[Math.floor(gaps.length / 2)];
|
|
7052
|
+
if (coarse) return gap >= 12 ? "year" : gap >= 3 ? "quarter" : "month";
|
|
7053
|
+
return gap >= 360 ? "year" : gap >= 85 ? "quarter" : gap >= 28 ? "month" : gap >= 7 ? "week" : "day";
|
|
7054
|
+
}
|
|
7055
|
+
var SEASON_LENGTH = { day: 7, week: 52, month: 12, quarter: 4, year: 1 };
|
|
7056
|
+
function buildForecastModel(data, x, y, seriesKey, period, options, singleName, otherLabel) {
|
|
7057
|
+
if (!x || !y || !Array.isArray(data)) return null;
|
|
7058
|
+
const points = [];
|
|
7059
|
+
for (const row of data) {
|
|
7060
|
+
if (!row || typeof row !== "object") continue;
|
|
7061
|
+
const date = parseDate(row[x]);
|
|
7062
|
+
const value = toChartNumber(row[y]);
|
|
7063
|
+
if (!date || value === null) continue;
|
|
7064
|
+
const raw = seriesKey ? row[seriesKey] : singleName;
|
|
7065
|
+
const name = raw === null || raw === void 0 || raw === "" ? "\u2014" : String(raw);
|
|
7066
|
+
points.push({ date, name, value });
|
|
7067
|
+
}
|
|
7068
|
+
if (points.length === 0) return null;
|
|
7069
|
+
const grain = period ?? inferPeriod(points.map((p) => p.date));
|
|
7070
|
+
const step = grain === "week" ? 7 : 1;
|
|
7071
|
+
const keyed = points.map((p) => ({ ...p, key: ordinal(p.date, grain) }));
|
|
7072
|
+
let min = Math.min(...keyed.map((p) => p.key));
|
|
7073
|
+
const max = Math.max(...keyed.map((p) => p.key));
|
|
7074
|
+
if ((max - min) / step + 1 > MAX_PERIODS) min = max - (MAX_PERIODS - 1) * step;
|
|
7075
|
+
const count = Math.floor((max - min) / step) + 1;
|
|
7076
|
+
const slot = (key) => Math.floor((key - min) / step);
|
|
7077
|
+
const sums = /* @__PURE__ */ new Map();
|
|
7078
|
+
for (const p of keyed) {
|
|
7079
|
+
const at = slot(p.key);
|
|
7080
|
+
if (at < 0) continue;
|
|
7081
|
+
let list = sums.get(p.name);
|
|
7082
|
+
if (!list) {
|
|
7083
|
+
list = Array(count).fill(null);
|
|
7084
|
+
sums.set(p.name, list);
|
|
7085
|
+
}
|
|
7086
|
+
list[at] = (list[at] ?? 0) + p.value;
|
|
7087
|
+
}
|
|
7088
|
+
const all = [...sums.keys()];
|
|
7089
|
+
const totals = all.map((name) => ({ name, value: sums.get(name).reduce((a, v) => a + (v ?? 0), 0) }));
|
|
7090
|
+
const { top, rest, other } = aggregateTopN(totals, void 0, otherLabel);
|
|
7091
|
+
const names = top.map((t) => t.name);
|
|
7092
|
+
const actual = names.map((n) => sums.get(n));
|
|
7093
|
+
if (other) {
|
|
7094
|
+
const folded = Array(count).fill(null);
|
|
7095
|
+
for (const r of rest) sums.get(r.name).forEach((v, i) => v === null ? void 0 : folded[i] = (folded[i] ?? 0) + v);
|
|
7096
|
+
names.push(other.name);
|
|
7097
|
+
actual.push(folded);
|
|
7098
|
+
}
|
|
7099
|
+
const resolved = { ...options, seasonLength: options?.seasonLength ?? SEASON_LENGTH[grain] };
|
|
7100
|
+
const results = actual.map((values) => {
|
|
7101
|
+
try {
|
|
7102
|
+
return forecast(values.map((v) => v === null ? NaN : v), resolved);
|
|
7103
|
+
} catch {
|
|
7104
|
+
return null;
|
|
7105
|
+
}
|
|
7106
|
+
});
|
|
7107
|
+
const length = Math.max(count, ...results.map((r) => r ? r.end + r.points.length : 0));
|
|
7108
|
+
const labels = Array.from({ length }, (_, i) => ordinalLabel(min + i * step, grain));
|
|
7109
|
+
const predicted = results.map((r) => {
|
|
7110
|
+
const out = Array(length).fill(null);
|
|
7111
|
+
r?.points.forEach((p, k) => out[r.end + k] = p);
|
|
7112
|
+
return out;
|
|
7113
|
+
});
|
|
7114
|
+
return {
|
|
7115
|
+
labels,
|
|
7116
|
+
actualCount: count,
|
|
7117
|
+
names,
|
|
7118
|
+
otherName: other ? other.name : null,
|
|
7119
|
+
actual: actual.map((list) => [...list, ...Array(length - count).fill(null)]),
|
|
7120
|
+
predicted,
|
|
7121
|
+
fittedEnd: results.map((r) => r ? r.end - 1 : -1),
|
|
7122
|
+
results
|
|
7123
|
+
};
|
|
7124
|
+
}
|
|
7125
|
+
var FORECAST_ID = "__forecast:";
|
|
7126
|
+
var BAND_BASE_ID = "__forecast-band-base:";
|
|
7127
|
+
var BAND_RANGE_ID = "__forecast-band-range:";
|
|
7128
|
+
function ForecastChartImpl({
|
|
7129
|
+
data = NO_ROWS14,
|
|
7130
|
+
x,
|
|
7131
|
+
y,
|
|
7132
|
+
series: seriesKey,
|
|
7133
|
+
period,
|
|
7134
|
+
forecast: forecast2,
|
|
7135
|
+
forecastLabel,
|
|
7136
|
+
forecastSource,
|
|
7137
|
+
forecastNote,
|
|
7138
|
+
colors,
|
|
7139
|
+
labels,
|
|
7140
|
+
valueFormatter,
|
|
7141
|
+
format,
|
|
7142
|
+
title,
|
|
7143
|
+
summary,
|
|
7144
|
+
actions,
|
|
7145
|
+
fileName,
|
|
7146
|
+
isLoading,
|
|
7147
|
+
emptyState,
|
|
7148
|
+
height,
|
|
7149
|
+
chartOptions,
|
|
7150
|
+
showLegend = true,
|
|
7151
|
+
showTooltip = true,
|
|
7152
|
+
showGridLines = true,
|
|
7153
|
+
showXAxis = true,
|
|
7154
|
+
showYAxis = true,
|
|
7155
|
+
yAxisWidth,
|
|
7156
|
+
minValue,
|
|
7157
|
+
maxValue,
|
|
7158
|
+
fitToData = false,
|
|
7159
|
+
xAxisLabel,
|
|
7160
|
+
yAxisLabel,
|
|
7161
|
+
ariaLabel,
|
|
7162
|
+
className,
|
|
7163
|
+
...rest
|
|
7164
|
+
}, ref) {
|
|
7165
|
+
const { ref: frameRef, surface, width: plotWidth, tokens } = useChartSurface(ref);
|
|
7166
|
+
const locale = useLocale();
|
|
7167
|
+
const t = TEXT19[uiLanguage(locale)];
|
|
7168
|
+
const fcLabel = forecastLabel ?? t.forecast;
|
|
7169
|
+
const tooltipFormatter = useMemo(() => resolveValueFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7170
|
+
const axisFormatter = useMemo(() => resolveAxisFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7171
|
+
const axisTicks = useMemo(() => resolveAxisTickFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7172
|
+
const singleName = labels?.[y] ?? y ?? "";
|
|
7173
|
+
const model = useMemo(
|
|
7174
|
+
() => buildForecastModel(data, x, y, seriesKey, period, forecast2, singleName, otherSeriesLabel(locale)),
|
|
7175
|
+
// forecast はオブジェクトリテラルで渡されることが多いので中身で比べる
|
|
7176
|
+
[data, x, y, seriesKey, period, JSON.stringify(forecast2 ?? null), singleName, locale]
|
|
7177
|
+
);
|
|
7178
|
+
const palette = useMemo(
|
|
7179
|
+
() => model ? resolveChartColors(seriesColorsFor(model.names, colors, CHART_OTHER_COLOR, model.otherName), tokens) : [],
|
|
7180
|
+
[model, colors, tokens]
|
|
7181
|
+
);
|
|
7182
|
+
const option = useMemo(() => {
|
|
7183
|
+
if (!model) return null;
|
|
7184
|
+
const index = "__period";
|
|
7185
|
+
const rows = model.labels.map((label3, i) => {
|
|
7186
|
+
const row = { [index]: label3 };
|
|
7187
|
+
model.names.forEach((name, c) => row[name] = model.actual[c][i]);
|
|
7188
|
+
return row;
|
|
7189
|
+
});
|
|
7190
|
+
const base = buildCartesianOption({
|
|
7191
|
+
kind: "line",
|
|
7192
|
+
data: rows,
|
|
7193
|
+
index,
|
|
7194
|
+
categories: model.names,
|
|
7195
|
+
palette,
|
|
7196
|
+
valueFormatter: tooltipFormatter,
|
|
7197
|
+
axisFormatter,
|
|
7198
|
+
axisTicks,
|
|
7199
|
+
curveType: "linear",
|
|
7200
|
+
connectNulls: false,
|
|
7201
|
+
showLegend,
|
|
7202
|
+
showTooltip,
|
|
7203
|
+
showGridLines,
|
|
7204
|
+
showXAxis,
|
|
7205
|
+
showYAxis,
|
|
7206
|
+
startEndOnly: false,
|
|
7207
|
+
yAxisWidth,
|
|
7208
|
+
minValue,
|
|
7209
|
+
maxValue,
|
|
7210
|
+
fitToData,
|
|
7211
|
+
allowDecimals: true,
|
|
7212
|
+
xAxisLabel,
|
|
7213
|
+
yAxisLabel,
|
|
7214
|
+
showDots: false,
|
|
7215
|
+
active: null,
|
|
7216
|
+
highlight: "none",
|
|
7217
|
+
reserveActions: Boolean(actions),
|
|
7218
|
+
guideColor: tokens?.mutedForeground ?? void 0,
|
|
7219
|
+
labelBackground: surface,
|
|
7220
|
+
labelColor: tokens?.foreground ?? void 0,
|
|
7221
|
+
plotWidth,
|
|
7222
|
+
plotHeight: typeof height === "number" ? height : void 0,
|
|
7223
|
+
locale
|
|
7224
|
+
});
|
|
7225
|
+
const bandAlpha = isDarkSurface(surface) ? FORECAST_BAND_ALPHA_DARK : FORECAST_BAND_ALPHA_LIGHT;
|
|
7226
|
+
const helper = {
|
|
7227
|
+
type: "line",
|
|
7228
|
+
symbol: "none",
|
|
7229
|
+
showSymbol: false,
|
|
7230
|
+
connectNulls: true,
|
|
7231
|
+
legendHoverLink: false,
|
|
7232
|
+
emphasis: { disabled: true }
|
|
7233
|
+
};
|
|
7234
|
+
const lines = [];
|
|
7235
|
+
const bands = [];
|
|
7236
|
+
model.names.forEach((name, c) => {
|
|
7237
|
+
const start = model.fittedEnd[c];
|
|
7238
|
+
if (start < 0) return;
|
|
7239
|
+
const color = palette[c] || void 0;
|
|
7240
|
+
const predicted = model.predicted[c];
|
|
7241
|
+
const startValue = model.actual[c][start] ?? null;
|
|
7242
|
+
const line = predicted.map((p, i) => i === start ? startValue : p?.value ?? null);
|
|
7243
|
+
const lower = predicted.map((p, i) => i === start ? startValue : p ? Math.min(p.lower, p.upper) : null);
|
|
7244
|
+
const range = predicted.map((p, i) => i === start ? startValue === null ? null : 0 : p ? Math.abs(p.upper - p.lower) : null);
|
|
7245
|
+
lines.push({
|
|
7246
|
+
...helper,
|
|
7247
|
+
id: FORECAST_ID + name,
|
|
7248
|
+
name: fcLabel,
|
|
7249
|
+
z: 3,
|
|
7250
|
+
lineStyle: { width: 2, type: FORECAST_LINE_DASH, color, opacity: 0.7 },
|
|
7251
|
+
itemStyle: { color, opacity: 0.7 },
|
|
7252
|
+
data: line
|
|
7253
|
+
});
|
|
7254
|
+
bands.push(
|
|
7255
|
+
{ ...helper, id: BAND_BASE_ID + name, name: fcLabel, silent: true, z: 1, stack: BAND_BASE_ID + name, tooltip: { show: false }, lineStyle: { opacity: 0 }, itemStyle: { color }, data: lower },
|
|
7256
|
+
{
|
|
7257
|
+
...helper,
|
|
7258
|
+
id: BAND_RANGE_ID + name,
|
|
7259
|
+
name: fcLabel,
|
|
7260
|
+
silent: true,
|
|
7261
|
+
z: 1,
|
|
7262
|
+
stack: BAND_BASE_ID + name,
|
|
7263
|
+
tooltip: { show: false },
|
|
7264
|
+
lineStyle: { opacity: 0 },
|
|
7265
|
+
itemStyle: { color },
|
|
7266
|
+
areaStyle: color ? { color: withAlpha(color, bandAlpha), opacity: 1 } : { opacity: bandAlpha },
|
|
7267
|
+
data: range
|
|
7268
|
+
}
|
|
7269
|
+
);
|
|
7270
|
+
});
|
|
7271
|
+
if (lines.length === 0) return mergeChartOptions(base, chartOptions);
|
|
7272
|
+
const boundary = Math.max(...model.fittedEnd);
|
|
7273
|
+
const total = model.labels.length;
|
|
7274
|
+
const span = plotWidth && total > 1 ? (plotWidth - 64) / (total - 1) * (total - 1 - boundary) : null;
|
|
7275
|
+
const boundaryText = forecastBoundaryText(fcLabel);
|
|
7276
|
+
lines[0] = {
|
|
7277
|
+
...lines[0],
|
|
7278
|
+
markLine: {
|
|
7279
|
+
silent: true,
|
|
7280
|
+
symbol: "none",
|
|
7281
|
+
animation: false,
|
|
7282
|
+
data: [
|
|
7283
|
+
{
|
|
7284
|
+
name: "forecast-boundary",
|
|
7285
|
+
xAxis: boundary,
|
|
7286
|
+
lineStyle: { type: "dashed", width: 1, color: tokens?.mutedForeground ?? void 0 },
|
|
7287
|
+
label: {
|
|
7288
|
+
show: true,
|
|
7289
|
+
position: "end",
|
|
7290
|
+
rotate: 0,
|
|
7291
|
+
align: forecastLabelAlign(span, boundaryText),
|
|
7292
|
+
verticalAlign: "bottom",
|
|
7293
|
+
distance: 4,
|
|
7294
|
+
fontSize: 12,
|
|
7295
|
+
color: tokens?.mutedForeground ?? void 0,
|
|
7296
|
+
formatter: boundaryText
|
|
7297
|
+
}
|
|
7298
|
+
}
|
|
7299
|
+
]
|
|
7300
|
+
}
|
|
7301
|
+
};
|
|
7302
|
+
const legend = base.legend;
|
|
7303
|
+
const grid = base.grid;
|
|
7304
|
+
const times = timeTooltipLabels(model.labels, locale);
|
|
7305
|
+
const pct = `${Math.round((forecast2?.confidence && forecast2.confidence > 0 && forecast2.confidence < 1 ? forecast2.confidence : 0.95) * 100)}%`;
|
|
7306
|
+
const option2 = {
|
|
7307
|
+
...base,
|
|
7308
|
+
legend: legend ? { ...legend, data: [...legend.data ?? [], { name: fcLabel, icon: LEGEND_DASHED_ICON }] } : legend,
|
|
7309
|
+
grid: grid ? { ...grid, top: (grid.top ?? 12) + BOUNDARY_LABEL_ROOM } : grid,
|
|
7310
|
+
tooltip: {
|
|
7311
|
+
...base.tooltip,
|
|
7312
|
+
formatter: (params) => {
|
|
7313
|
+
const list = Array.isArray(params) ? params : [params];
|
|
7314
|
+
const i = list[0]?.dataIndex ?? 0;
|
|
7315
|
+
const shown = new Set(list.map((p) => p.seriesId ?? ""));
|
|
7316
|
+
const visible = (id) => list.length === 0 || shown.has(id);
|
|
7317
|
+
const rows2 = [];
|
|
7318
|
+
model.names.forEach((name, c) => {
|
|
7319
|
+
const color = palette[c] || "currentColor";
|
|
7320
|
+
const value = model.actual[c][i] ?? null;
|
|
7321
|
+
if (value !== null && visible(`series:${name}`)) rows2.push({ name, color, value });
|
|
7322
|
+
const p = model.predicted[c][i];
|
|
7323
|
+
if (p && visible(FORECAST_ID + name)) {
|
|
7324
|
+
rows2.push({
|
|
7325
|
+
name: model.names.length > 1 ? `${name} \xB7 ${fcLabel}` : fcLabel,
|
|
7326
|
+
color,
|
|
7327
|
+
value: p.value,
|
|
7328
|
+
note: t.interval(pct, tooltipFormatter(Math.min(p.lower, p.upper)), tooltipFormatter(Math.max(p.lower, p.upper)))
|
|
7329
|
+
});
|
|
7330
|
+
}
|
|
7331
|
+
});
|
|
7332
|
+
return tooltipHtml(times?.[i] ?? model.labels[i] ?? "", rows2, tooltipFormatter, [], locale);
|
|
7333
|
+
}
|
|
7334
|
+
},
|
|
7335
|
+
series: [...base.series ?? [], ...lines, ...bands]
|
|
7336
|
+
};
|
|
7337
|
+
return mergeChartOptions(option2, chartOptions);
|
|
7338
|
+
}, [
|
|
7339
|
+
model,
|
|
7340
|
+
palette,
|
|
7341
|
+
tooltipFormatter,
|
|
7342
|
+
axisFormatter,
|
|
7343
|
+
axisTicks,
|
|
7344
|
+
showLegend,
|
|
7345
|
+
showTooltip,
|
|
7346
|
+
showGridLines,
|
|
7347
|
+
showXAxis,
|
|
7348
|
+
showYAxis,
|
|
7349
|
+
yAxisWidth,
|
|
7350
|
+
minValue,
|
|
7351
|
+
maxValue,
|
|
7352
|
+
fitToData,
|
|
7353
|
+
xAxisLabel,
|
|
7354
|
+
yAxisLabel,
|
|
7355
|
+
actions,
|
|
7356
|
+
tokens,
|
|
7357
|
+
surface,
|
|
7358
|
+
plotWidth,
|
|
7359
|
+
height,
|
|
7360
|
+
locale,
|
|
7361
|
+
fcLabel,
|
|
7362
|
+
forecast2?.confidence,
|
|
7363
|
+
t,
|
|
7364
|
+
chartOptions
|
|
7365
|
+
]);
|
|
7366
|
+
const dataTable = useMemo(() => {
|
|
7367
|
+
if (!model) return void 0;
|
|
7368
|
+
const forecastColumns = model.names.map((name) => `${name} (${fcLabel})`);
|
|
7369
|
+
const rows = model.labels.map((label3, i) => {
|
|
7370
|
+
const row = { period: label3 };
|
|
7371
|
+
model.names.forEach((name, c) => {
|
|
7372
|
+
row[name] = model.actual[c][i];
|
|
7373
|
+
row[forecastColumns[c]] = model.predicted[c][i]?.value ?? null;
|
|
7374
|
+
});
|
|
7375
|
+
return row;
|
|
7376
|
+
});
|
|
7377
|
+
const columns = model.names.flatMap((name, c) => model.fittedEnd[c] >= 0 ? [name, forecastColumns[c]] : [name]);
|
|
7378
|
+
const table = chartDataTable(rows, "period", columns, tooltipFormatter, void 0, t.item);
|
|
7379
|
+
const times = timeTooltipLabels(model.labels, locale);
|
|
7380
|
+
return times ? { ...table, rows: table.rows.map((row, i) => [times[i] ?? row[0] ?? "", ...row.slice(1)]) } : table;
|
|
7381
|
+
}, [model, fcLabel, tooltipFormatter, t, locale]);
|
|
7382
|
+
const label2 = ariaLabel ?? t.chart(model?.names ?? [singleName]);
|
|
7383
|
+
return /* @__PURE__ */ jsx(
|
|
7384
|
+
ChartFrame,
|
|
7385
|
+
{
|
|
7386
|
+
...chartRootProps(rest),
|
|
7387
|
+
ref: frameRef,
|
|
7388
|
+
title,
|
|
7389
|
+
summary,
|
|
7390
|
+
isLoading: isLoading ?? false,
|
|
7391
|
+
isEmpty: !model || !option,
|
|
7392
|
+
emptyState,
|
|
7393
|
+
height,
|
|
7394
|
+
variant: "line",
|
|
7395
|
+
ariaLabel: label2,
|
|
7396
|
+
className,
|
|
7397
|
+
dataTable,
|
|
7398
|
+
footnote: [t.note(forecastSource), forecastNote].filter(Boolean).join(" "),
|
|
7399
|
+
children: option && /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary } })
|
|
7400
|
+
}
|
|
7401
|
+
);
|
|
7402
|
+
}
|
|
7403
|
+
var ForecastChart = forwardRef(ForecastChartImpl);
|
|
7404
|
+
ForecastChart.displayName = "ForecastChart";
|
|
7405
|
+
|
|
7406
|
+
// ../squadkit/registry/lib/color-ramp.ts
|
|
7407
|
+
var RAMP_FLOOR_SHARE = 0.2;
|
|
7408
|
+
var RAMP_LIGHT_TEXT_FROM = 0.55;
|
|
7409
|
+
function parseRgb2(color) {
|
|
7410
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
7411
|
+
if (hex) {
|
|
7412
|
+
const n = parseInt(hex[1], 16);
|
|
7413
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
7414
|
+
}
|
|
7415
|
+
const rgb = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(color.trim());
|
|
7416
|
+
return rgb ? [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])] : null;
|
|
7417
|
+
}
|
|
7418
|
+
var toLinear2 = (c) => (c /= 255) <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
7419
|
+
var fromLinear2 = (c) => Math.round(255 * Math.min(1, Math.max(0, c <= 31308e-7 ? c * 12.92 : 1.055 * c ** (1 / 2.4) - 0.055)));
|
|
7420
|
+
function toOklab2([r, g, b]) {
|
|
7421
|
+
const [lr, lg, lb] = [toLinear2(r), toLinear2(g), toLinear2(b)];
|
|
7422
|
+
const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
|
|
7423
|
+
const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
|
|
7424
|
+
const t = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
|
|
7425
|
+
return [0.2104542553 * l + 0.793617785 * m - 0.0040720468 * t, 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * t, 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * t];
|
|
7426
|
+
}
|
|
7427
|
+
function fromOklab2([L, a, b]) {
|
|
7428
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
7429
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
7430
|
+
const t = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
7431
|
+
const rgb = [4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * t, -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * t, -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * t];
|
|
7432
|
+
return `rgb(${rgb.map(fromLinear2).join(", ")})`;
|
|
7433
|
+
}
|
|
7434
|
+
function withAlpha3(color, alpha) {
|
|
7435
|
+
const c = parseRgb2(color);
|
|
7436
|
+
return c ? `rgba(${c[0]}, ${c[1]}, ${c[2]}, ${alpha})` : color;
|
|
7437
|
+
}
|
|
7438
|
+
function mixIntoSurface2(color, surface, share) {
|
|
7439
|
+
const c = parseRgb2(color);
|
|
7440
|
+
const s = surface ? parseRgb2(surface) : null;
|
|
7441
|
+
if (!c || !s) return withAlpha3(color, share);
|
|
7442
|
+
const [c1, c2] = [toOklab2(c), toOklab2(s)];
|
|
7443
|
+
return fromOklab2([0, 1, 2].map((i) => c2[i] + (c1[i] - c2[i]) * share));
|
|
7444
|
+
}
|
|
7445
|
+
function rampRatio(value, min, max) {
|
|
7446
|
+
return max > min ? Math.min(1, Math.max(0, (value - min) / (max - min))) : 1;
|
|
7447
|
+
}
|
|
7448
|
+
function rampColor(color, surface, ratio) {
|
|
7449
|
+
return mixIntoSurface2(color, surface, RAMP_FLOOR_SHARE + (1 - RAMP_FLOOR_SHARE) * Math.min(1, Math.max(0, ratio)));
|
|
7450
|
+
}
|
|
7451
|
+
function rampGradientCss(tone) {
|
|
7452
|
+
return `linear-gradient(to right, color-mix(in oklch, var(--${tone}) ${RAMP_FLOOR_SHARE * 100}%, var(--card)), var(--${tone}))`;
|
|
7453
|
+
}
|
|
7454
|
+
var GEO_MAPS = ["japan", "us", "world"];
|
|
7455
|
+
var geoMapKey = (name) => `squadkit-${name}`;
|
|
7456
|
+
var GEO_ASPECT_SCALE = { japan: 0.8, us: 0.8, world: 0.75 };
|
|
7457
|
+
function fold(text) {
|
|
7458
|
+
return text.normalize("NFKC").normalize("NFD").replace(/[̀-ͯ]/g, "").normalize("NFC").toLowerCase().trim();
|
|
7459
|
+
}
|
|
7460
|
+
var SEPARATORS = /[\s.,'’`"()_\-‐・・]/g;
|
|
7461
|
+
var squash = (text) => text.replace(SEPARATORS, "");
|
|
7462
|
+
function regionKeys(raw) {
|
|
7463
|
+
if (raw === null || raw === void 0) return [];
|
|
7464
|
+
const text = fold(String(raw));
|
|
7465
|
+
if (!text) return [];
|
|
7466
|
+
const keys = [squash(text)];
|
|
7467
|
+
const stripped = text.replace(/[\s-]+(prefecture|pref|ken|fu|to|state)$/, "").replace(/^(state of|the)\s+/, "").replace(/(?<=..)(都|府|県|州)$/, "");
|
|
7468
|
+
if (stripped !== text) keys.push(squash(stripped));
|
|
7469
|
+
if (/^\d{1,2}$/.test(text)) keys.push(`jp${text.padStart(2, "0")}`);
|
|
7470
|
+
return [...new Set(keys.filter(Boolean))];
|
|
7471
|
+
}
|
|
7472
|
+
var ALIASES = {
|
|
7473
|
+
"US-DC": ["District of Columbia", "Washington DC", "Washington, D.C.", "\u30EF\u30B7\u30F3\u30C8\u30F3DC", "\u30B3\u30ED\u30F3\u30D3\u30A2\u7279\u5225\u533A"],
|
|
7474
|
+
USA: ["United States", "US", "USA", "America", "\u7C73\u56FD", "\u30A2\u30E1\u30EA\u30AB"],
|
|
7475
|
+
GBR: ["UK", "Great Britain", "Britain", "\u82F1\u56FD"],
|
|
7476
|
+
KOR: ["Korea", "Republic of Korea", "Korea, Republic of", "\u97D3\u56FD"],
|
|
7477
|
+
PRK: ["DPRK", "\u5317\u671D\u9BAE"],
|
|
7478
|
+
CHN: ["China", "\u4E2D\u56FD"],
|
|
7479
|
+
TWN: ["TW", "\u53F0\u6E7E"],
|
|
7480
|
+
RUS: ["Russian Federation"],
|
|
7481
|
+
CZE: ["Czechia"],
|
|
7482
|
+
VNM: ["Viet Nam"],
|
|
7483
|
+
CIV: ["C\xF4te d'Ivoire"],
|
|
7484
|
+
SWZ: ["Swaziland"],
|
|
7485
|
+
MKD: ["Macedonia"],
|
|
7486
|
+
COD: ["DR Congo", "Congo-Kinshasa"],
|
|
7487
|
+
COG: ["Congo", "Congo-Brazzaville"]
|
|
7488
|
+
};
|
|
7489
|
+
var SKIP_NAME = /* @__PURE__ */ new Set(["US-DC"]);
|
|
7490
|
+
var indexCache = /* @__PURE__ */ new WeakMap();
|
|
7491
|
+
function buildGeoIndex(map, collection) {
|
|
7492
|
+
const cached = indexCache.get(collection);
|
|
7493
|
+
if (cached) return cached;
|
|
7494
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7495
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
7496
|
+
const add = (key, code) => {
|
|
7497
|
+
for (const k of regionKeys(key)) if (!byKey.has(k)) byKey.set(k, code);
|
|
7498
|
+
};
|
|
7499
|
+
for (const { properties: p } of collection.features) {
|
|
7500
|
+
byCode.set(p.code, p);
|
|
7501
|
+
add(p.code, p.code);
|
|
7502
|
+
const short = p.code.split("-")[1];
|
|
7503
|
+
if (short) add(short, p.code);
|
|
7504
|
+
if (p.alpha2) add(p.alpha2, p.code);
|
|
7505
|
+
if (map === "japan" && short) add(String(Number(short)), p.code);
|
|
7506
|
+
}
|
|
7507
|
+
for (const { properties: p } of collection.features) {
|
|
7508
|
+
if (!SKIP_NAME.has(p.code)) add(p.name, p.code);
|
|
7509
|
+
add(p.name_ja, p.code);
|
|
7510
|
+
for (const alias of ALIASES[p.code] ?? []) add(alias, p.code);
|
|
7511
|
+
}
|
|
7512
|
+
const index = { map, byKey, byCode };
|
|
7513
|
+
indexCache.set(collection, index);
|
|
7514
|
+
return index;
|
|
7515
|
+
}
|
|
7516
|
+
function matchRegion(index, raw) {
|
|
7517
|
+
for (const key of regionKeys(raw)) {
|
|
7518
|
+
const code = index.byKey.get(key);
|
|
7519
|
+
if (code) return code;
|
|
7520
|
+
}
|
|
7521
|
+
return null;
|
|
7522
|
+
}
|
|
7523
|
+
function regionName(properties, language) {
|
|
7524
|
+
return language === "ja" && properties.name_ja ? properties.name_ja : properties.name.replace(/ Prefecture$/, "");
|
|
7525
|
+
}
|
|
7526
|
+
function detectGeoMap(values, loaded2) {
|
|
7527
|
+
const distinct = [...new Set(values.filter((v) => v !== null && v !== void 0 && String(v).trim() !== "").map(String))];
|
|
7528
|
+
let best = null;
|
|
7529
|
+
let bestCount = 0;
|
|
7530
|
+
for (const map of GEO_MAPS) {
|
|
7531
|
+
const collection = loaded2[map];
|
|
7532
|
+
if (!collection) continue;
|
|
7533
|
+
const index = buildGeoIndex(map, collection);
|
|
7534
|
+
const count = distinct.filter((v) => matchRegion(index, v) !== null).length;
|
|
7535
|
+
if (count > bestCount) [best, bestCount] = [map, count];
|
|
7536
|
+
}
|
|
7537
|
+
return best;
|
|
7538
|
+
}
|
|
7539
|
+
var BOUNDS = {
|
|
7540
|
+
japan: [122, 20, 154, 46.5],
|
|
7541
|
+
us: [-180, 18, -65, 72]
|
|
7542
|
+
};
|
|
7543
|
+
function detectBasemap(points) {
|
|
7544
|
+
if (points.length === 0) return "world";
|
|
7545
|
+
for (const map of ["japan", "us"]) {
|
|
7546
|
+
const [w, s, e, n] = BOUNDS[map];
|
|
7547
|
+
if (points.every(([lon, lat]) => lon >= w && lon <= e && lat >= s && lat <= n)) return map;
|
|
7548
|
+
}
|
|
7549
|
+
return "world";
|
|
7550
|
+
}
|
|
7551
|
+
var US_INSETS = [
|
|
7552
|
+
// Alaska (the Aleutians stop short of the antimeridian in this data).
|
|
7553
|
+
{ box: [-180, 51, -129.5, 72], center: [-149, 63], to: [-117, 25.5], scale: 0.35 },
|
|
7554
|
+
// Hawaii.
|
|
7555
|
+
{ box: [-161, 18.5, -154.5, 22.5], center: [-157.5, 20.5], to: [-106, 25.5], scale: 1 }
|
|
7556
|
+
];
|
|
7557
|
+
function insetUsPoint(lon, lat) {
|
|
7558
|
+
for (const inset of US_INSETS) {
|
|
7559
|
+
const [w, s, e, n] = inset.box;
|
|
7560
|
+
if (lon >= w && lon <= e && lat >= s && lat <= n) {
|
|
7561
|
+
return [inset.to[0] + (lon - inset.center[0]) * inset.scale, inset.to[1] + (lat - inset.center[1]) * inset.scale];
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
return [lon, lat];
|
|
7565
|
+
}
|
|
7566
|
+
function insetUs(collection) {
|
|
7567
|
+
const moveRing = (ring) => ring.map(([lon, lat]) => insetUsPoint(lon, lat));
|
|
7568
|
+
return {
|
|
7569
|
+
type: "FeatureCollection",
|
|
7570
|
+
features: collection.features.map((f) => {
|
|
7571
|
+
if (f.properties.code !== "US-AK" && f.properties.code !== "US-HI") return f;
|
|
7572
|
+
const g = f.geometry;
|
|
7573
|
+
const geometry = g.type === "Polygon" ? { type: "Polygon", coordinates: g.coordinates.map(moveRing) } : { type: "MultiPolygon", coordinates: g.coordinates.map((poly) => poly.map(moveRing)) };
|
|
7574
|
+
return { ...f, geometry };
|
|
7575
|
+
})
|
|
7576
|
+
};
|
|
7577
|
+
}
|
|
7578
|
+
var loading = /* @__PURE__ */ new Map();
|
|
7579
|
+
var loaded = /* @__PURE__ */ new Map();
|
|
7580
|
+
function loadedGeoMap(name) {
|
|
7581
|
+
return loaded.get(name);
|
|
7582
|
+
}
|
|
7583
|
+
function loadGeoMap(name) {
|
|
7584
|
+
const pending = loading.get(name);
|
|
7585
|
+
if (pending) return pending;
|
|
7586
|
+
const promise = (name === "japan" ? import('./geo-japan-7BMSOJRX.js').then((m) => m.JAPAN_PREFECTURES) : name === "us" ? import('./geo-us-GVOG7VXH.js').then((m) => insetUs(m.US_STATES)) : import('./geo-world-35HL56UU.js').then((m) => m.WORLD_COUNTRIES)).then((collection) => {
|
|
7587
|
+
registerMap(geoMapKey(name), collection);
|
|
7588
|
+
loaded.set(name, collection);
|
|
7589
|
+
return collection;
|
|
7590
|
+
});
|
|
7591
|
+
promise.catch(() => loading.delete(name));
|
|
7592
|
+
loading.set(name, promise);
|
|
7593
|
+
return promise;
|
|
7594
|
+
}
|
|
7595
|
+
async function loadGeoMapFor(values, map) {
|
|
7596
|
+
if (map !== "auto") return { name: map, collection: await loadGeoMap(map) };
|
|
7597
|
+
const distinct = [...new Set(values.filter((v) => v !== null && v !== void 0).map(String))];
|
|
7598
|
+
const [japan, us] = await Promise.all([loadGeoMap("japan"), loadGeoMap("us")]);
|
|
7599
|
+
const local = { japan, us };
|
|
7600
|
+
const pick = detectGeoMap(distinct, local);
|
|
7601
|
+
if (pick) {
|
|
7602
|
+
const index = buildGeoIndex(pick, local[pick]);
|
|
7603
|
+
const matched = distinct.filter((v) => matchRegion(index, v) !== null).length;
|
|
7604
|
+
if (matched * 2 >= distinct.length) return { name: pick, collection: local[pick] };
|
|
7605
|
+
}
|
|
7606
|
+
const world = await loadGeoMap("world");
|
|
7607
|
+
const name = detectGeoMap(distinct, { ...local, world }) ?? "world";
|
|
7608
|
+
return { name, collection: name === "world" ? world : local[name] };
|
|
7609
|
+
}
|
|
7610
|
+
function useGeoMap(values, map) {
|
|
7611
|
+
const key = useMemo(() => [...new Set(values.map((v) => v === null || v === void 0 ? "" : String(v)))].join("\0"), [values]);
|
|
7612
|
+
const [state, setState] = useState(null);
|
|
7613
|
+
const wanted = `${map}${key}`;
|
|
7614
|
+
useEffect(() => {
|
|
7615
|
+
let alive = true;
|
|
7616
|
+
loadGeoMapFor(key ? key.split("\0") : [], map).then(
|
|
7617
|
+
(result) => alive && setState({ key: wanted, result }),
|
|
7618
|
+
() => {
|
|
7619
|
+
}
|
|
7620
|
+
);
|
|
7621
|
+
return () => {
|
|
7622
|
+
alive = false;
|
|
7623
|
+
};
|
|
7624
|
+
}, [wanted]);
|
|
7625
|
+
if (state?.key === wanted) return state.result;
|
|
7626
|
+
const ready = map !== "auto" ? loadedGeoMap(map) : void 0;
|
|
7627
|
+
return ready && map !== "auto" ? { name: map, collection: ready } : null;
|
|
7628
|
+
}
|
|
7629
|
+
use([MapChart]);
|
|
7630
|
+
function buildFilledMapModel(config) {
|
|
7631
|
+
const { data, region, value, map, collection } = config;
|
|
7632
|
+
const language = uiLanguage(resolveLocale(config.locale));
|
|
7633
|
+
const index = buildGeoIndex(map, collection);
|
|
7634
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
7635
|
+
const unmatched = [];
|
|
7636
|
+
for (const row of data) {
|
|
7637
|
+
if (!row || typeof row !== "object") continue;
|
|
7638
|
+
const code = matchRegion(index, row[region]);
|
|
7639
|
+
if (code) byCode.set(code, row);
|
|
7640
|
+
else unmatched.push(row);
|
|
7641
|
+
}
|
|
7642
|
+
let lo = Infinity;
|
|
7643
|
+
let hi = -Infinity;
|
|
7644
|
+
const regions = collection.features.map(({ properties }) => {
|
|
7645
|
+
const row = byCode.get(properties.code) ?? null;
|
|
7646
|
+
const n = row ? toChartNumber(row[value]) : null;
|
|
7647
|
+
if (n !== null) [lo, hi] = [Math.min(lo, n), Math.max(hi, n)];
|
|
7648
|
+
const key = row ? String(row[region]) : null;
|
|
7649
|
+
return { code: properties.code, key, name: (key !== null ? config.labels?.[key] : void 0) ?? regionName(properties, language), value: n, row };
|
|
7650
|
+
});
|
|
7651
|
+
const min = config.scale?.min ?? (Number.isFinite(lo) ? lo : 0);
|
|
7652
|
+
let max = config.scale?.max ?? (Number.isFinite(hi) ? hi : 1);
|
|
7653
|
+
if (max <= min) max = min + 1;
|
|
7654
|
+
const selectedCodes = config.selected && config.selected.length > 0 ? new Set(config.selected.map((v) => matchRegion(index, v)).filter((c) => c !== null)) : null;
|
|
7655
|
+
return { map, regions, unmatched, selectedCodes, min, max };
|
|
7656
|
+
}
|
|
7657
|
+
function escapeHtml4(text) {
|
|
7658
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7659
|
+
}
|
|
7660
|
+
var TEXT20 = {
|
|
7661
|
+
ja: {
|
|
7662
|
+
join: "\u30FB",
|
|
7663
|
+
region: "\u5730\u57DF",
|
|
7664
|
+
notOnMap: "(\u5730\u56F3\u306B\u7121\u3044)",
|
|
7665
|
+
top: (name, value) => `\u6700\u5927\u306F ${name} \u306E ${value}`,
|
|
7666
|
+
chart: (value) => `${value}\u306E\u5730\u56F3`
|
|
7667
|
+
},
|
|
7668
|
+
en: {
|
|
7669
|
+
join: ", ",
|
|
7670
|
+
region: "Region",
|
|
7671
|
+
notOnMap: " (not on the map)",
|
|
7672
|
+
top: (name, value) => `Highest: ${name} (${value})`,
|
|
7673
|
+
chart: (value) => `Map of ${value}`
|
|
7674
|
+
}
|
|
7675
|
+
};
|
|
7676
|
+
var textOf7 = (locale) => TEXT20[uiLanguage(resolveLocale(locale))];
|
|
7677
|
+
function detailText(raw, formatter) {
|
|
7678
|
+
if (raw === null || raw === void 0 || raw === "") return EMPTY_VALUE;
|
|
7679
|
+
const n = typeof raw === "string" ? null : toChartNumber(raw);
|
|
7680
|
+
return n === null ? String(raw) : formatter(n);
|
|
7681
|
+
}
|
|
7682
|
+
function buildFilledMapOption(config) {
|
|
7683
|
+
const { model, labels, valueFormatter } = config;
|
|
7684
|
+
const t = textOf7(config.locale);
|
|
7685
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7686
|
+
const base = config.color || "#6366f1";
|
|
7687
|
+
const border = config.surfaceColor ?? "#ffffff";
|
|
7688
|
+
const dimmed = (r) => model.selectedCodes !== null && !model.selectedCodes.has(r.code);
|
|
7689
|
+
const tooltip = (r) => {
|
|
7690
|
+
const head = `${escapeHtml4(r.name)} ${r.value === null ? EMPTY_VALUE : escapeHtml4(valueFormatter(r.value))}`;
|
|
7691
|
+
const extras = (config.details ?? []).map((key) => `${escapeHtml4(label2(key))} ${escapeHtml4(detailText(r.row?.[key], valueFormatter))}`).join(t.join);
|
|
7692
|
+
return extras ? `${head}${t.join}${extras}` : head;
|
|
7693
|
+
};
|
|
7694
|
+
const option = {
|
|
7695
|
+
animation: false,
|
|
7696
|
+
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.region) } : { show: false },
|
|
7697
|
+
series: [
|
|
7698
|
+
{
|
|
7699
|
+
id: "regions",
|
|
7700
|
+
type: "map",
|
|
7701
|
+
map: geoMapKey(model.map),
|
|
7702
|
+
nameProperty: "code",
|
|
7703
|
+
aspectScale: GEO_ASPECT_SCALE[model.map],
|
|
7704
|
+
roam: false,
|
|
7705
|
+
selectedMode: false,
|
|
7706
|
+
left: 8,
|
|
7707
|
+
right: 8,
|
|
7708
|
+
top: 8,
|
|
7709
|
+
bottom: 8,
|
|
7710
|
+
label: { show: false },
|
|
7711
|
+
itemStyle: { areaColor: config.emptyColor, borderColor: border, borderWidth: 0.6 },
|
|
7712
|
+
emphasis: { label: { show: false }, itemStyle: { borderColor: config.hoverColor, borderWidth: 1.2 } },
|
|
7713
|
+
data: model.regions.map((r) => {
|
|
7714
|
+
const fill = r.value === null ? config.emptyColor : rampColor(base, config.surfaceColor, rampRatio(r.value, model.min, model.max));
|
|
7715
|
+
const areaColor = dimmed(r) ? mixIntoSurface2(fill, config.surfaceColor, 0.3) : fill;
|
|
7716
|
+
return {
|
|
7717
|
+
name: r.code,
|
|
7718
|
+
value: r.value ?? void 0,
|
|
7719
|
+
region: r,
|
|
7720
|
+
itemStyle: { areaColor },
|
|
7721
|
+
emphasis: { itemStyle: { areaColor } }
|
|
7722
|
+
};
|
|
7723
|
+
})
|
|
7724
|
+
}
|
|
7725
|
+
]
|
|
7726
|
+
};
|
|
7727
|
+
return config.chartOptions ? mergeDeep3(option, config.chartOptions) : option;
|
|
7728
|
+
}
|
|
7729
|
+
function mergeDeep3(a, b) {
|
|
7730
|
+
const out = { ...a };
|
|
7731
|
+
for (const [k, v] of Object.entries(b)) {
|
|
7732
|
+
const cur = out[k];
|
|
7733
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep3(cur, v) : v;
|
|
7734
|
+
}
|
|
7735
|
+
return out;
|
|
7736
|
+
}
|
|
7737
|
+
function filledMapDataTable(model, names, formatter, details = [], labels, locale) {
|
|
7738
|
+
const t = textOf7(locale);
|
|
7739
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7740
|
+
const cells = (row, value) => [
|
|
7741
|
+
value === null ? EMPTY_VALUE : formatter(value),
|
|
7742
|
+
...details.map((key) => detailText(row?.[key], formatter))
|
|
7743
|
+
];
|
|
7744
|
+
const rows = [
|
|
7745
|
+
...model.regions.filter((r) => r.row !== null).map((r) => [r.name, ...cells(r.row, r.value)]),
|
|
7746
|
+
...model.unmatched.map((row) => [`${label2(String(row[names.region] ?? EMPTY_VALUE))}${t.notOnMap}`, ...cells(row, toChartNumber(row[names.value]))])
|
|
7747
|
+
];
|
|
7748
|
+
return { columns: [labels?.[names.region] ?? t.region, label2(names.value), ...details.map(label2)], rows };
|
|
7749
|
+
}
|
|
7750
|
+
function buildFilledMapSummary(model, formatter, title, locale) {
|
|
7751
|
+
let best;
|
|
7752
|
+
for (const r of model.regions) if (r.value !== null && (!best || r.value > best.value)) best = r;
|
|
7753
|
+
if (!best) return void 0;
|
|
7754
|
+
return `${title ? `${title}: ` : ""}${textOf7(locale).top(best.name, formatter(best.value))}`;
|
|
7755
|
+
}
|
|
7756
|
+
function colorsFor2(tokens, tone) {
|
|
7757
|
+
const [color = "", empty = "", hover = ""] = resolveChartColors([tone, "var(--muted)", "var(--foreground)"], tokens);
|
|
7758
|
+
return { color, empty, hover };
|
|
7759
|
+
}
|
|
7760
|
+
var NO_ROWS15 = [];
|
|
7761
|
+
function FilledMapImpl({
|
|
7762
|
+
data = NO_ROWS15,
|
|
7763
|
+
region,
|
|
7764
|
+
value,
|
|
7765
|
+
map = "auto",
|
|
7766
|
+
labels,
|
|
7767
|
+
format,
|
|
7768
|
+
valueFormatter,
|
|
7769
|
+
tone = "chart-1",
|
|
7770
|
+
scale,
|
|
7771
|
+
showLegend = true,
|
|
7772
|
+
showTooltip = true,
|
|
7773
|
+
details,
|
|
7774
|
+
selected,
|
|
7775
|
+
onSelect,
|
|
7776
|
+
title,
|
|
7777
|
+
summary,
|
|
7778
|
+
isLoading,
|
|
7779
|
+
emptyState,
|
|
7780
|
+
height,
|
|
7781
|
+
chartOptions,
|
|
7782
|
+
actions,
|
|
7783
|
+
fileName,
|
|
7784
|
+
className,
|
|
7785
|
+
ariaLabel,
|
|
7786
|
+
...rest
|
|
7787
|
+
}, ref) {
|
|
7788
|
+
const rows = Array.isArray(data) ? data : NO_ROWS15;
|
|
7789
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
7790
|
+
const locale = useLocale();
|
|
7791
|
+
const formatter = useMemo(() => resolveValueFormatter(valueFormatter, format ?? "number", locale), [valueFormatter, format, locale]);
|
|
7792
|
+
const regionValues = useMemo(() => rows.map((row) => row && typeof row === "object" ? row[region] : void 0), [rows, region]);
|
|
7793
|
+
const geo = useGeoMap(regionValues, map);
|
|
7794
|
+
const model = useMemo(
|
|
7795
|
+
() => geo ? buildFilledMapModel({ data: rows, region, value, map: geo.name, collection: geo.collection, scale, labels, selected, locale }) : null,
|
|
7796
|
+
[geo, rows, region, value, scale?.min, scale?.max, labels, selected, locale]
|
|
7797
|
+
);
|
|
7798
|
+
const colors = useMemo(() => colorsFor2(tokens, tone), [tokens, tone]);
|
|
7799
|
+
const option = useMemo(
|
|
7800
|
+
() => model ? buildFilledMapOption({
|
|
7801
|
+
model,
|
|
7802
|
+
color: colors.color,
|
|
7803
|
+
surfaceColor: surface ?? void 0,
|
|
7804
|
+
emptyColor: colors.empty || "rgba(128, 128, 128, 0.15)",
|
|
7805
|
+
hoverColor: colors.hover || "#000000",
|
|
7806
|
+
labels,
|
|
7807
|
+
valueFormatter: formatter,
|
|
7808
|
+
details,
|
|
7809
|
+
showTooltip,
|
|
7810
|
+
chartOptions,
|
|
7811
|
+
locale
|
|
7812
|
+
}) : null,
|
|
7813
|
+
[model, value, colors, surface, labels, formatter, details, showTooltip, chartOptions, locale]
|
|
7814
|
+
);
|
|
7815
|
+
const onEvents = useMemo(
|
|
7816
|
+
() => onSelect ? {
|
|
7817
|
+
click: (params) => {
|
|
7818
|
+
const r = params.data?.region;
|
|
7819
|
+
if (r?.row) onSelect(String(r.row[region]), r.row);
|
|
7820
|
+
}
|
|
7821
|
+
} : void 0,
|
|
7822
|
+
[onSelect, region]
|
|
7823
|
+
);
|
|
7824
|
+
const dataTable = useMemo(
|
|
7825
|
+
() => model ? filledMapDataTable(model, { region, value }, formatter, details, labels, locale) : void 0,
|
|
7826
|
+
[model, region, value, formatter, details, labels, locale]
|
|
7827
|
+
);
|
|
7828
|
+
const resolvedSummary = useMemo(
|
|
7829
|
+
() => summary === "auto" ? model ? buildFilledMapSummary(model, formatter, title, locale) : void 0 : summary,
|
|
7830
|
+
[summary, model, formatter, title, locale]
|
|
7831
|
+
);
|
|
7832
|
+
const label2 = ariaLabel ?? textOf7(locale).chart(labels?.[value] ?? value ?? "");
|
|
7833
|
+
const isEmpty = rows.length === 0;
|
|
7834
|
+
return /* @__PURE__ */ jsx(
|
|
7835
|
+
ChartFrame,
|
|
7836
|
+
{
|
|
7837
|
+
...chartRootProps(rest),
|
|
7838
|
+
ref: frameRef,
|
|
7839
|
+
title,
|
|
7840
|
+
summary: resolvedSummary,
|
|
7841
|
+
isLoading: (isLoading ?? false) || !isEmpty && !option,
|
|
7842
|
+
isEmpty,
|
|
7843
|
+
emptyState,
|
|
7844
|
+
height,
|
|
7845
|
+
variant: "bar",
|
|
7846
|
+
ariaLabel: label2,
|
|
7847
|
+
className,
|
|
7848
|
+
dataTable,
|
|
7849
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
7850
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: option && /* @__PURE__ */ jsx(EChart, { option, onEvents, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } }) }),
|
|
7851
|
+
showLegend && model ? /* @__PURE__ */ jsx(RampLegend, { min: model.min, max: model.max, formatter, tone }) : null
|
|
7852
|
+
] })
|
|
7853
|
+
}
|
|
7854
|
+
);
|
|
7855
|
+
}
|
|
7856
|
+
function RampLegend({ min, max, formatter, tone }) {
|
|
7857
|
+
return /* @__PURE__ */ jsx("div", { "data-slot": "filled-map-legend", className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground tabular-nums", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
7858
|
+
/* @__PURE__ */ jsx("span", { children: formatter(min) }),
|
|
7859
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
7860
|
+
/* @__PURE__ */ jsx("span", { children: formatter(max) })
|
|
7861
|
+
] }) });
|
|
7862
|
+
}
|
|
7863
|
+
var FilledMap = forwardRef(FilledMapImpl);
|
|
7864
|
+
FilledMap.displayName = "FilledMap";
|
|
7865
|
+
use([ScatterChart$1, GeoComponent]);
|
|
7866
|
+
var SYMBOL_MAP_MIN_SIZE = 6;
|
|
7867
|
+
var SYMBOL_MAP_MAX_SIZE = 36;
|
|
7868
|
+
var SYMBOL_MAP_DEFAULT_SIZE = 10;
|
|
7869
|
+
var MAX_CATEGORIES = 5;
|
|
7870
|
+
var isBlank = (v) => v === null || v === void 0 || typeof v === "string" && v.trim() === "";
|
|
7871
|
+
function buildSymbolMapModel(config) {
|
|
7872
|
+
const { data, latitude, longitude, name, size, color } = config;
|
|
7873
|
+
const rows = data.filter((row) => row && typeof row === "object");
|
|
7874
|
+
const colorValues = color ? rows.map((row) => row[color]).filter((v) => !isBlank(v)) : [];
|
|
7875
|
+
const colorMode = !color || colorValues.length === 0 ? "none" : colorValues.every((v) => toChartNumber(v) !== null) ? "numeric" : "category";
|
|
7876
|
+
const points = [];
|
|
7877
|
+
const unplotted = [];
|
|
7878
|
+
const categories = [];
|
|
7879
|
+
let [lo, hi, sizeMax] = [Infinity, -Infinity, 0];
|
|
7880
|
+
for (const row of rows) {
|
|
7881
|
+
const lat = toChartNumber(row[latitude]);
|
|
7882
|
+
const lon = toChartNumber(row[longitude]);
|
|
7883
|
+
if (lat === null || lon === null || Math.abs(lat) > 90 || Math.abs(lon) > 180) {
|
|
7884
|
+
unplotted.push(row);
|
|
7885
|
+
continue;
|
|
7886
|
+
}
|
|
7887
|
+
const s = size ? toChartNumber(row[size]) : null;
|
|
7888
|
+
if (s !== null && s > sizeMax) sizeMax = s;
|
|
7889
|
+
const shade = colorMode === "numeric" ? toChartNumber(row[color]) : null;
|
|
7890
|
+
if (shade !== null) [lo, hi] = [Math.min(lo, shade), Math.max(hi, shade)];
|
|
7891
|
+
const category = colorMode === "category" && !isBlank(row[color]) ? String(row[color]) : null;
|
|
7892
|
+
if (category !== null && !categories.includes(category)) categories.push(category);
|
|
7893
|
+
points.push({ lon, lat, name: name && !isBlank(row[name]) ? String(row[name]) : null, size: s, shade, category, row });
|
|
7894
|
+
}
|
|
7895
|
+
const shadeMin = Number.isFinite(lo) ? lo : 0;
|
|
7896
|
+
let shadeMax = Number.isFinite(hi) ? hi : 1;
|
|
7897
|
+
if (shadeMax <= shadeMin) shadeMax = shadeMin + 1;
|
|
7898
|
+
return { points, unplotted, colorMode, categories, shadeMin, shadeMax, sizeMax };
|
|
7899
|
+
}
|
|
7900
|
+
function symbolMapSize(value, max, hasSize) {
|
|
7901
|
+
if (!hasSize) return SYMBOL_MAP_DEFAULT_SIZE;
|
|
7902
|
+
if (value === null || value <= 0 || !(max > 0)) return SYMBOL_MAP_MIN_SIZE;
|
|
7903
|
+
return SYMBOL_MAP_MIN_SIZE + (SYMBOL_MAP_MAX_SIZE - SYMBOL_MAP_MIN_SIZE) * Math.sqrt(Math.min(1, value / max));
|
|
7904
|
+
}
|
|
7905
|
+
function escapeHtml5(text) {
|
|
7906
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7907
|
+
}
|
|
7908
|
+
var TEXT21 = {
|
|
7909
|
+
ja: {
|
|
7910
|
+
join: "\u30FB",
|
|
7911
|
+
point: "\u5730\u70B9",
|
|
7912
|
+
latitude: "\u7DEF\u5EA6",
|
|
7913
|
+
longitude: "\u7D4C\u5EA6",
|
|
7914
|
+
notPlotted: "(\u5EA7\u6A19\u306A\u3057)",
|
|
7915
|
+
chart: "\u5730\u70B9\u306E\u5730\u56F3"
|
|
7916
|
+
},
|
|
7917
|
+
en: {
|
|
7918
|
+
join: ", ",
|
|
7919
|
+
point: "Point",
|
|
7920
|
+
latitude: "Latitude",
|
|
7921
|
+
longitude: "Longitude",
|
|
7922
|
+
notPlotted: " (no coordinates)",
|
|
7923
|
+
chart: "Map of points"
|
|
7924
|
+
}
|
|
7925
|
+
};
|
|
7926
|
+
var textOf8 = (locale) => TEXT21[uiLanguage(resolveLocale(locale))];
|
|
7927
|
+
var coordText = (n) => n === null ? EMPTY_VALUE : n.toFixed(4).replace(/\.?0+$/, "");
|
|
7928
|
+
function buildSymbolMapOption(config) {
|
|
7929
|
+
const { model, labels, valueFormatter, sizeFormatter } = config;
|
|
7930
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7931
|
+
const base = config.toneColor || "#6366f1";
|
|
7932
|
+
const selected = config.selected && config.selected.length > 0 ? new Set(config.selected.map(String)) : null;
|
|
7933
|
+
const fillOf = (p) => {
|
|
7934
|
+
if (model.colorMode === "numeric") return p.shade === null ? config.landColor : rampColor(base, config.surfaceColor, rampRatio(p.shade, model.shadeMin, model.shadeMax));
|
|
7935
|
+
if (model.colorMode === "category") {
|
|
7936
|
+
const at = p.category === null ? -1 : model.categories.indexOf(p.category);
|
|
7937
|
+
return at === -1 ? config.categoryColors[MAX_CATEGORIES] ?? base : config.categoryColors[Math.min(at, MAX_CATEGORIES)] ?? base;
|
|
7938
|
+
}
|
|
7939
|
+
return base;
|
|
7940
|
+
};
|
|
7941
|
+
const tooltip = (p) => {
|
|
7942
|
+
const lines = [escapeHtml5(p.name ?? `${coordText(p.lat)}, ${coordText(p.lon)}`)];
|
|
7943
|
+
if (config.size) lines.push(`${escapeHtml5(label2(config.size))} ${p.size === null ? EMPTY_VALUE : escapeHtml5(sizeFormatter(p.size))}`);
|
|
7944
|
+
if (config.color && config.color !== config.size)
|
|
7945
|
+
lines.push(`${escapeHtml5(label2(config.color))} ${model.colorMode === "numeric" ? p.shade === null ? EMPTY_VALUE : escapeHtml5(valueFormatter(p.shade)) : escapeHtml5(label2(p.category ?? EMPTY_VALUE))}`);
|
|
7946
|
+
for (const key of config.details ?? []) lines.push(`${escapeHtml5(label2(key))} ${escapeHtml5(cellText(p.row[key], valueFormatter))}`);
|
|
7947
|
+
return lines.join("<br/>");
|
|
7948
|
+
};
|
|
7949
|
+
const order = [...model.points].sort((a, b) => (b.size ?? 0) - (a.size ?? 0));
|
|
7950
|
+
const option = {
|
|
7951
|
+
animation: false,
|
|
7952
|
+
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.point) } : { show: false },
|
|
7953
|
+
geo: {
|
|
7954
|
+
map: geoMapKey(config.basemap),
|
|
7955
|
+
silent: true,
|
|
7956
|
+
roam: false,
|
|
7957
|
+
aspectScale: GEO_ASPECT_SCALE[config.basemap],
|
|
7958
|
+
left: 8,
|
|
7959
|
+
right: 8,
|
|
7960
|
+
top: 8,
|
|
7961
|
+
bottom: 8,
|
|
7962
|
+
label: { show: false },
|
|
7963
|
+
itemStyle: { areaColor: config.landColor, borderColor: config.borderColor, borderWidth: 0.6 },
|
|
7964
|
+
emphasis: { disabled: true }
|
|
7965
|
+
},
|
|
7966
|
+
series: [
|
|
7967
|
+
{
|
|
7968
|
+
id: "points",
|
|
7969
|
+
type: "scatter",
|
|
7970
|
+
coordinateSystem: "geo",
|
|
7971
|
+
data: order.map((p) => {
|
|
7972
|
+
const [x, y] = config.basemap === "us" ? insetUsPoint(p.lon, p.lat) : [p.lon, p.lat];
|
|
7973
|
+
const fill = fillOf(p);
|
|
7974
|
+
const dim = selected !== null && !(p.name !== null && selected.has(p.name));
|
|
7975
|
+
return {
|
|
7976
|
+
value: [x, y],
|
|
7977
|
+
point: p,
|
|
7978
|
+
symbolSize: symbolMapSize(p.size, model.sizeMax, !!config.size),
|
|
7979
|
+
itemStyle: { color: dim ? mixIntoSurface2(fill, config.surfaceColor, 0.3) : fill, opacity: dim ? 1 : 0.85 }
|
|
7980
|
+
};
|
|
7981
|
+
}),
|
|
7982
|
+
itemStyle: { borderColor: config.surfaceColor ?? "#ffffff", borderWidth: 1 },
|
|
7983
|
+
emphasis: { scale: false, itemStyle: { opacity: 1, borderColor: config.borderColor, borderWidth: 1.5 } }
|
|
7984
|
+
}
|
|
7985
|
+
]
|
|
7986
|
+
};
|
|
7987
|
+
return config.chartOptions ? mergeDeep4(option, config.chartOptions) : option;
|
|
7988
|
+
}
|
|
7989
|
+
function cellText(raw, formatter) {
|
|
7990
|
+
if (isBlank(raw)) return EMPTY_VALUE;
|
|
7991
|
+
const n = typeof raw === "string" ? null : toChartNumber(raw);
|
|
7992
|
+
return n === null ? String(raw) : formatter(n);
|
|
7993
|
+
}
|
|
7994
|
+
function mergeDeep4(a, b) {
|
|
7995
|
+
const out = { ...a };
|
|
7996
|
+
for (const [k, v] of Object.entries(b)) {
|
|
7997
|
+
const cur = out[k];
|
|
7998
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep4(cur, v) : v;
|
|
7999
|
+
}
|
|
8000
|
+
return out;
|
|
8001
|
+
}
|
|
8002
|
+
function symbolMapDataTable(model, names, formatters, details = [], labels, locale) {
|
|
8003
|
+
const t = textOf8(locale);
|
|
8004
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8005
|
+
const extra = [names.size, names.color].filter((k) => !!k && k !== names.latitude && k !== names.longitude);
|
|
8006
|
+
const keys = [.../* @__PURE__ */ new Set([...extra, ...details])];
|
|
8007
|
+
const rowCells = (row) => keys.map((key) => key === names.size ? cellText(row[key], formatters.size) : key === names.color && model.colorMode === "category" ? isBlank(row[key]) ? EMPTY_VALUE : label2(String(row[key])) : cellText(row[key], formatters.value));
|
|
8008
|
+
const nameOf = (row) => names.name && !isBlank(row[names.name]) ? String(row[names.name]) : EMPTY_VALUE;
|
|
8009
|
+
const rows = [
|
|
8010
|
+
...model.points.map((p) => [nameOf(p.row), coordText(p.lat), coordText(p.lon), ...rowCells(p.row)]),
|
|
8011
|
+
...model.unplotted.map((row) => [
|
|
8012
|
+
`${nameOf(row)}${t.notPlotted}`,
|
|
8013
|
+
coordText(toChartNumber(row[names.latitude])),
|
|
8014
|
+
coordText(toChartNumber(row[names.longitude])),
|
|
8015
|
+
...rowCells(row)
|
|
8016
|
+
])
|
|
8017
|
+
];
|
|
8018
|
+
return {
|
|
8019
|
+
columns: [names.name ? label2(names.name) : t.point, labels?.[names.latitude] ?? t.latitude, labels?.[names.longitude] ?? t.longitude, ...keys.map(label2)],
|
|
8020
|
+
rows
|
|
8021
|
+
};
|
|
8022
|
+
}
|
|
8023
|
+
function colorsFor3(tokens, tone) {
|
|
8024
|
+
const resolved = resolveChartColors([tone, "var(--muted)", "var(--border)", ...DEFAULT_CHART_COLORS, CHART_OTHER_COLOR], tokens);
|
|
8025
|
+
const [toneColor = "", land = "", border = ""] = resolved;
|
|
8026
|
+
return { toneColor, land, border, categories: resolved.slice(3) };
|
|
8027
|
+
}
|
|
8028
|
+
var NO_ROWS16 = [];
|
|
8029
|
+
function SymbolMapImpl({
|
|
8030
|
+
data = NO_ROWS16,
|
|
8031
|
+
latitude,
|
|
8032
|
+
longitude,
|
|
8033
|
+
name,
|
|
8034
|
+
size,
|
|
8035
|
+
color,
|
|
8036
|
+
basemap = "auto",
|
|
8037
|
+
labels,
|
|
8038
|
+
format,
|
|
8039
|
+
sizeFormat,
|
|
8040
|
+
tone = "chart-1",
|
|
8041
|
+
showLegend = true,
|
|
8042
|
+
showTooltip = true,
|
|
8043
|
+
details,
|
|
8044
|
+
selected,
|
|
8045
|
+
onSelect,
|
|
8046
|
+
title,
|
|
8047
|
+
summary,
|
|
8048
|
+
isLoading,
|
|
8049
|
+
emptyState,
|
|
8050
|
+
height,
|
|
8051
|
+
chartOptions,
|
|
8052
|
+
actions,
|
|
8053
|
+
fileName,
|
|
8054
|
+
className,
|
|
8055
|
+
ariaLabel,
|
|
8056
|
+
...rest
|
|
8057
|
+
}, ref) {
|
|
8058
|
+
const rows = Array.isArray(data) ? data : NO_ROWS16;
|
|
8059
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
8060
|
+
const locale = useLocale();
|
|
8061
|
+
const formatter = useMemo(() => resolveValueFormatter(void 0, format ?? "number", locale), [format, locale]);
|
|
8062
|
+
const sizeFormatter = useMemo(() => resolveValueFormatter(void 0, sizeFormat ?? format ?? "number", locale), [sizeFormat, format, locale]);
|
|
8063
|
+
const model = useMemo(() => buildSymbolMapModel({ data: rows, latitude, longitude, name, size, color }), [rows, latitude, longitude, name, size, color]);
|
|
8064
|
+
const resolvedBasemap = useMemo(
|
|
8065
|
+
() => basemap === "auto" ? detectBasemap(model.points.map((p) => [p.lon, p.lat])) : basemap,
|
|
8066
|
+
[basemap, model]
|
|
8067
|
+
);
|
|
8068
|
+
const geo = useGeoMap(NO_ROWS16, resolvedBasemap);
|
|
8069
|
+
const colors = useMemo(() => colorsFor3(tokens, tone), [tokens, tone]);
|
|
8070
|
+
const option = useMemo(
|
|
8071
|
+
() => geo ? buildSymbolMapOption({
|
|
8072
|
+
model,
|
|
8073
|
+
basemap: geo.name,
|
|
8074
|
+
size,
|
|
8075
|
+
color,
|
|
8076
|
+
toneColor: colors.toneColor,
|
|
8077
|
+
categoryColors: colors.categories,
|
|
8078
|
+
surfaceColor: surface ?? void 0,
|
|
8079
|
+
landColor: colors.land || "rgba(128, 128, 128, 0.15)",
|
|
8080
|
+
borderColor: colors.border || "rgba(128, 128, 128, 0.4)",
|
|
8081
|
+
labels,
|
|
8082
|
+
valueFormatter: formatter,
|
|
8083
|
+
sizeFormatter,
|
|
8084
|
+
details,
|
|
8085
|
+
selected,
|
|
8086
|
+
showTooltip,
|
|
8087
|
+
chartOptions}) : null,
|
|
8088
|
+
[geo, model, size, color, colors, surface, labels, formatter, sizeFormatter, details, selected, showTooltip, chartOptions, locale]
|
|
8089
|
+
);
|
|
8090
|
+
const onEvents = useMemo(
|
|
8091
|
+
() => onSelect ? {
|
|
8092
|
+
click: (params) => {
|
|
8093
|
+
const p = params.data?.point;
|
|
8094
|
+
if (p) onSelect(p.row);
|
|
8095
|
+
}
|
|
8096
|
+
} : void 0,
|
|
8097
|
+
[onSelect]
|
|
8098
|
+
);
|
|
8099
|
+
const dataTable = useMemo(
|
|
8100
|
+
() => symbolMapDataTable(model, { latitude, longitude, name, size, color }, { value: formatter, size: sizeFormatter }, details, labels, locale),
|
|
8101
|
+
[model, latitude, longitude, name, size, color, formatter, sizeFormatter, details, labels, locale]
|
|
8102
|
+
);
|
|
8103
|
+
const label2 = ariaLabel ?? textOf8(locale).chart;
|
|
8104
|
+
const isEmpty = rows.length === 0;
|
|
8105
|
+
const legendCategories = model.categories.length > MAX_CATEGORIES ? [...model.categories.slice(0, MAX_CATEGORIES), otherSeriesLabel(locale)] : model.categories;
|
|
8106
|
+
const legend = !showLegend ? null : model.colorMode === "numeric" ? /* @__PURE__ */ jsx("div", { "data-slot": "symbol-map-legend", className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground tabular-nums", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
8107
|
+
color && /* @__PURE__ */ jsx("span", { children: labels?.[color] ?? color }),
|
|
8108
|
+
/* @__PURE__ */ jsx("span", { children: formatter(model.shadeMin) }),
|
|
8109
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
8110
|
+
/* @__PURE__ */ jsx("span", { children: formatter(model.shadeMax) })
|
|
8111
|
+
] }) }) : model.colorMode === "category" ? /* @__PURE__ */ jsx(Legend, { categories: legendCategories, labels }) : null;
|
|
8112
|
+
return /* @__PURE__ */ jsx(
|
|
8113
|
+
ChartFrame,
|
|
8114
|
+
{
|
|
8115
|
+
...chartRootProps(rest),
|
|
8116
|
+
ref: frameRef,
|
|
8117
|
+
title,
|
|
8118
|
+
summary,
|
|
8119
|
+
isLoading: (isLoading ?? false) || !isEmpty && !option,
|
|
8120
|
+
isEmpty,
|
|
8121
|
+
emptyState,
|
|
8122
|
+
height,
|
|
8123
|
+
variant: "bar",
|
|
8124
|
+
ariaLabel: label2,
|
|
8125
|
+
className,
|
|
8126
|
+
dataTable,
|
|
8127
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
8128
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: option && /* @__PURE__ */ jsx(EChart, { option, onEvents, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary } }) }),
|
|
8129
|
+
legend
|
|
8130
|
+
] })
|
|
8131
|
+
}
|
|
8132
|
+
);
|
|
8133
|
+
}
|
|
8134
|
+
var SymbolMap = forwardRef(SymbolMapImpl);
|
|
8135
|
+
SymbolMap.displayName = "SymbolMap";
|
|
8136
|
+
use([TreemapChart]);
|
|
8137
|
+
var MAX_CATEGORIES2 = 5;
|
|
8138
|
+
function buildTreemapModel(config) {
|
|
8139
|
+
const { data, index, group, value, color } = config;
|
|
8140
|
+
const leaves = [];
|
|
8141
|
+
let [lo, hi] = [Infinity, -Infinity];
|
|
8142
|
+
for (const row of data) {
|
|
8143
|
+
if (!row || typeof row !== "object") continue;
|
|
8144
|
+
const shade = color ? toChartNumber(row[color]) : null;
|
|
8145
|
+
if (shade !== null) [lo, hi] = [Math.min(lo, shade), Math.max(hi, shade)];
|
|
8146
|
+
leaves.push({
|
|
8147
|
+
name: String(row[index] ?? EMPTY_VALUE),
|
|
8148
|
+
group: group ? String(row[group] ?? EMPTY_VALUE) : null,
|
|
8149
|
+
value: toChartNumber(row[value]),
|
|
8150
|
+
shade,
|
|
8151
|
+
row
|
|
8152
|
+
});
|
|
8153
|
+
}
|
|
8154
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
8155
|
+
for (const leaf of leaves) {
|
|
8156
|
+
if (leaf.value === null || leaf.value <= 0) continue;
|
|
8157
|
+
const list = byGroup.get(leaf.group) ?? [];
|
|
8158
|
+
list.push(leaf);
|
|
8159
|
+
byGroup.set(leaf.group, list);
|
|
8160
|
+
}
|
|
8161
|
+
const groups = [...byGroup.entries()].map(([name, list]) => ({ name, total: list.reduce((s, l) => s + l.value, 0), leaves: [...list].sort((a, b) => b.value - a.value) })).sort((a, b) => b.total - a.total);
|
|
8162
|
+
const total = groups.reduce((s, g) => s + g.total, 0);
|
|
8163
|
+
const categories = group ? groups.map((g) => g.name) : [...new Set(groups.flatMap((g) => g.leaves.map((l) => l.name)))];
|
|
8164
|
+
const shadeMin = Number.isFinite(lo) ? lo : 0;
|
|
8165
|
+
let shadeMax = Number.isFinite(hi) ? hi : 1;
|
|
8166
|
+
if (shadeMax <= shadeMin) shadeMax = shadeMin + 1;
|
|
8167
|
+
return { leaves, groups, categories, total, shadeMin, shadeMax };
|
|
8168
|
+
}
|
|
8169
|
+
function escapeHtml6(text) {
|
|
8170
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
8171
|
+
}
|
|
8172
|
+
var TEXT22 = {
|
|
8173
|
+
ja: {
|
|
8174
|
+
join: "\u30FB",
|
|
8175
|
+
item: "\u9805\u76EE",
|
|
8176
|
+
group: "\u30B0\u30EB\u30FC\u30D7",
|
|
8177
|
+
share: "\u69CB\u6210\u6BD4",
|
|
8178
|
+
top: (name, share) => `\u6700\u5927\u306F ${name}(\u5168\u4F53\u306E ${share})`,
|
|
8179
|
+
chart: (value) => `${value}\u306E\u30C4\u30EA\u30FC\u30DE\u30C3\u30D7`
|
|
8180
|
+
},
|
|
8181
|
+
en: {
|
|
8182
|
+
join: ", ",
|
|
8183
|
+
item: "Item",
|
|
8184
|
+
group: "Group",
|
|
8185
|
+
share: "Share",
|
|
8186
|
+
top: (name, share) => `Largest: ${name} (${share} of the total)`,
|
|
8187
|
+
chart: (value) => `Treemap of ${value}`
|
|
8188
|
+
}
|
|
8189
|
+
};
|
|
8190
|
+
var textOf9 = (locale) => TEXT22[uiLanguage(resolveLocale(locale))];
|
|
8191
|
+
function buildTreemapOption(config) {
|
|
8192
|
+
const { model, labels, valueFormatter } = config;
|
|
8193
|
+
const t = textOf9(config.locale);
|
|
8194
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8195
|
+
const surface = config.surfaceColor ?? "#ffffff";
|
|
8196
|
+
const base = config.toneColor || "#6366f1";
|
|
8197
|
+
const categoryColor = (name) => {
|
|
8198
|
+
const at = model.categories.indexOf(name);
|
|
8199
|
+
return config.categoryColors[at === -1 ? MAX_CATEGORIES2 : Math.min(at, MAX_CATEGORIES2)] ?? base;
|
|
8200
|
+
};
|
|
8201
|
+
const share = (v) => formatValue(model.total > 0 ? v / model.total : 0, "percent", config.locale);
|
|
8202
|
+
const leafNode = (leaf) => {
|
|
8203
|
+
let fill;
|
|
8204
|
+
let dark = true;
|
|
8205
|
+
if (config.colorMode === "numeric") {
|
|
8206
|
+
const ratio = leaf.shade === null ? 0 : rampRatio(leaf.shade, model.shadeMin, model.shadeMax);
|
|
8207
|
+
fill = rampColor(base, config.surfaceColor, ratio);
|
|
8208
|
+
dark = ratio >= RAMP_LIGHT_TEXT_FROM;
|
|
8209
|
+
} else fill = categoryColor(config.hasGroup ? leaf.group : leaf.name);
|
|
8210
|
+
return {
|
|
8211
|
+
name: label2(leaf.name),
|
|
8212
|
+
value: leaf.value,
|
|
8213
|
+
leaf,
|
|
8214
|
+
itemStyle: { color: fill },
|
|
8215
|
+
label: { color: dark ? surface : config.foregroundColor }
|
|
8216
|
+
};
|
|
8217
|
+
};
|
|
8218
|
+
const tooltip = (leaf, name, value) => {
|
|
8219
|
+
const head = leaf?.group != null ? `${escapeHtml6(label2(leaf.group))} / ${escapeHtml6(name)}` : escapeHtml6(name);
|
|
8220
|
+
const parts = [`${head} ${escapeHtml6(valueFormatter(value))}`, `${t.share} ${escapeHtml6(share(value))}`];
|
|
8221
|
+
if (leaf && config.colorKey && config.colorMode === "numeric")
|
|
8222
|
+
parts.push(`${escapeHtml6(label2(config.colorKey))} ${leaf.shade === null ? EMPTY_VALUE : escapeHtml6(config.colorFormatter(leaf.shade))}`);
|
|
8223
|
+
return parts.join(t.join);
|
|
8224
|
+
};
|
|
8225
|
+
const data = config.hasGroup ? model.groups.map((g) => ({
|
|
8226
|
+
name: label2(g.name),
|
|
8227
|
+
value: g.total,
|
|
8228
|
+
itemStyle: { color: config.colorMode === "numeric" ? config.foregroundColor : categoryColor(g.name) },
|
|
8229
|
+
children: g.leaves.map(leafNode)
|
|
8230
|
+
})) : model.groups.flatMap((g) => g.leaves.map(leafNode));
|
|
8231
|
+
const option = {
|
|
8232
|
+
animation: false,
|
|
8233
|
+
tooltip: config.showTooltip ? {
|
|
8234
|
+
trigger: "item",
|
|
8235
|
+
formatter: (p) => {
|
|
8236
|
+
const d = p;
|
|
8237
|
+
return tooltip(d.data?.leaf, d.name, Number(d.value));
|
|
8238
|
+
}
|
|
8239
|
+
} : { show: false },
|
|
8240
|
+
series: [
|
|
8241
|
+
{
|
|
8242
|
+
id: "tiles",
|
|
8243
|
+
type: "treemap",
|
|
8244
|
+
left: 0,
|
|
8245
|
+
right: 0,
|
|
8246
|
+
top: 0,
|
|
8247
|
+
bottom: 0,
|
|
8248
|
+
roam: false,
|
|
8249
|
+
nodeClick: false,
|
|
8250
|
+
breadcrumb: { show: false },
|
|
8251
|
+
squareRatio: 0.5 * (1 + Math.sqrt(5)),
|
|
8252
|
+
label: {
|
|
8253
|
+
show: true,
|
|
8254
|
+
position: "insideTopLeft",
|
|
8255
|
+
overflow: "truncate",
|
|
8256
|
+
lineHeight: 16,
|
|
8257
|
+
formatter: (p) => {
|
|
8258
|
+
const d = p;
|
|
8259
|
+
return `${d.name}
|
|
8260
|
+
${valueFormatter(Number(d.value))}`;
|
|
8261
|
+
}
|
|
8262
|
+
},
|
|
8263
|
+
upperLabel: { show: config.hasGroup, height: 20, color: surface, fontWeight: 600 },
|
|
8264
|
+
itemStyle: { borderColor: surface, borderWidth: 1, gapWidth: 1 },
|
|
8265
|
+
emphasis: { disabled: true },
|
|
8266
|
+
levels: config.hasGroup ? [{ itemStyle: { borderColor: surface, borderWidth: 2, gapWidth: 2 } }, { itemStyle: { borderColor: surface, borderWidth: 0, gapWidth: 1 } }] : [{ itemStyle: { borderColor: surface, borderWidth: 0, gapWidth: 1 } }],
|
|
8267
|
+
data
|
|
8268
|
+
}
|
|
8269
|
+
]
|
|
8270
|
+
};
|
|
8271
|
+
return config.chartOptions ? mergeDeep5(option, config.chartOptions) : option;
|
|
8272
|
+
}
|
|
8273
|
+
function mergeDeep5(a, b) {
|
|
8274
|
+
const out = { ...a };
|
|
8275
|
+
for (const [k, v] of Object.entries(b)) {
|
|
8276
|
+
const cur = out[k];
|
|
8277
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep5(cur, v) : v;
|
|
8278
|
+
}
|
|
8279
|
+
return out;
|
|
8280
|
+
}
|
|
8281
|
+
function treemapDataTable(model, names, formatters, labels, locale) {
|
|
8282
|
+
const t = textOf9(locale);
|
|
8283
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8284
|
+
const share = (v) => v === null || v <= 0 || model.total <= 0 ? EMPTY_VALUE : formatValue(v / model.total, "percent", locale);
|
|
8285
|
+
return {
|
|
8286
|
+
columns: [
|
|
8287
|
+
...names.group ? [labels?.[names.group] ?? t.group] : [],
|
|
8288
|
+
labels?.[names.index] ?? t.item,
|
|
8289
|
+
label2(names.value),
|
|
8290
|
+
t.share,
|
|
8291
|
+
...names.color && names.color !== names.value ? [label2(names.color)] : []
|
|
8292
|
+
],
|
|
8293
|
+
rows: model.leaves.map((leaf) => [
|
|
8294
|
+
...names.group ? [label2(leaf.group ?? EMPTY_VALUE)] : [],
|
|
8295
|
+
label2(leaf.name),
|
|
8296
|
+
leaf.value === null ? EMPTY_VALUE : formatters.value(leaf.value),
|
|
8297
|
+
share(leaf.value),
|
|
8298
|
+
...names.color && names.color !== names.value ? [leaf.shade === null ? EMPTY_VALUE : formatters.color(leaf.shade)] : []
|
|
8299
|
+
])
|
|
8300
|
+
};
|
|
8301
|
+
}
|
|
8302
|
+
function buildTreemapSummary(model, title, labels, locale) {
|
|
8303
|
+
let best;
|
|
8304
|
+
for (const g of model.groups) for (const l of g.leaves) if (!best || l.value > best.value) best = l;
|
|
8305
|
+
if (!best || model.total <= 0) return void 0;
|
|
8306
|
+
return `${title ? `${title}: ` : ""}${textOf9(locale).top(labels?.[best.name] ?? best.name, formatValue(best.value / model.total, "percent", locale))}`;
|
|
8307
|
+
}
|
|
8308
|
+
function colorsFor4(tokens, tone) {
|
|
8309
|
+
const resolved = resolveChartColors([tone, "var(--foreground)", ...DEFAULT_CHART_COLORS, CHART_OTHER_COLOR], tokens);
|
|
8310
|
+
const [toneColor = "", foreground = ""] = resolved;
|
|
8311
|
+
return { toneColor, foreground, categories: resolved.slice(2) };
|
|
8312
|
+
}
|
|
8313
|
+
var NO_ROWS17 = [];
|
|
8314
|
+
function TreemapImpl({
|
|
8315
|
+
data = NO_ROWS17,
|
|
8316
|
+
index,
|
|
8317
|
+
group,
|
|
8318
|
+
value,
|
|
8319
|
+
color,
|
|
8320
|
+
labels,
|
|
8321
|
+
format,
|
|
8322
|
+
colorFormat,
|
|
8323
|
+
valueFormatter,
|
|
8324
|
+
tone = "chart-1",
|
|
8325
|
+
showLegend = true,
|
|
8326
|
+
showTooltip = true,
|
|
8327
|
+
onSelect,
|
|
8328
|
+
title,
|
|
8329
|
+
summary,
|
|
8330
|
+
isLoading,
|
|
8331
|
+
emptyState,
|
|
8332
|
+
height,
|
|
8333
|
+
chartOptions,
|
|
8334
|
+
actions,
|
|
8335
|
+
fileName,
|
|
8336
|
+
className,
|
|
8337
|
+
ariaLabel,
|
|
8338
|
+
...rest
|
|
8339
|
+
}, ref) {
|
|
8340
|
+
const rows = Array.isArray(data) ? data : NO_ROWS17;
|
|
8341
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
8342
|
+
const locale = useLocale();
|
|
8343
|
+
const formatter = useMemo(() => resolveValueFormatter(valueFormatter, format ?? "number", locale), [valueFormatter, format, locale]);
|
|
8344
|
+
const colorFormatter = useMemo(() => resolveValueFormatter(void 0, colorFormat ?? format ?? "number", locale), [colorFormat, format, locale]);
|
|
8345
|
+
const model = useMemo(() => buildTreemapModel({ data: rows, index, group, value, color }), [rows, index, group, value, color]);
|
|
8346
|
+
const colorMode = color ? "numeric" : "category";
|
|
8347
|
+
const colors = useMemo(() => colorsFor4(tokens, tone), [tokens, tone]);
|
|
8348
|
+
const option = useMemo(
|
|
8349
|
+
() => buildTreemapOption({
|
|
8350
|
+
model,
|
|
8351
|
+
hasGroup: !!group,
|
|
8352
|
+
colorMode,
|
|
8353
|
+
toneColor: colors.toneColor,
|
|
8354
|
+
categoryColors: colors.categories,
|
|
8355
|
+
surfaceColor: surface ?? void 0,
|
|
8356
|
+
foregroundColor: colors.foreground || tokens?.foreground || "#111111",
|
|
8357
|
+
labels,
|
|
8358
|
+
valueFormatter: formatter,
|
|
8359
|
+
colorFormatter,
|
|
8360
|
+
colorKey: color,
|
|
8361
|
+
showTooltip,
|
|
8362
|
+
chartOptions,
|
|
8363
|
+
locale
|
|
8364
|
+
}),
|
|
8365
|
+
[model, group, colorMode, colors, surface, tokens, labels, formatter, colorFormatter, color, showTooltip, chartOptions, locale]
|
|
8366
|
+
);
|
|
8367
|
+
const onEvents = useMemo(
|
|
8368
|
+
() => onSelect ? {
|
|
8369
|
+
click: (params) => {
|
|
8370
|
+
const leaf = params.data?.leaf;
|
|
8371
|
+
if (leaf) onSelect(leaf.row);
|
|
8372
|
+
}
|
|
8373
|
+
} : void 0,
|
|
8374
|
+
[onSelect]
|
|
8375
|
+
);
|
|
8376
|
+
const dataTable = useMemo(
|
|
8377
|
+
() => treemapDataTable(model, { index, group, value, color }, { value: formatter, color: colorFormatter }, labels, locale),
|
|
8378
|
+
[model, index, group, value, color, formatter, colorFormatter, labels, locale]
|
|
8379
|
+
);
|
|
8380
|
+
const resolvedSummary = useMemo(
|
|
8381
|
+
() => summary === "auto" ? buildTreemapSummary(model, title, labels, locale) : summary,
|
|
8382
|
+
[summary, model, title, labels, locale]
|
|
8383
|
+
);
|
|
8384
|
+
const label2 = ariaLabel ?? textOf9(locale).chart(labels?.[value] ?? value ?? "");
|
|
8385
|
+
const legendCategories = model.categories.length > MAX_CATEGORIES2 ? [...model.categories.slice(0, MAX_CATEGORIES2), otherSeriesLabel(locale)] : model.categories;
|
|
8386
|
+
const legend = !showLegend ? null : colorMode === "numeric" ? /* @__PURE__ */ jsx("div", { "data-slot": "treemap-legend", className: "flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground tabular-nums", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
8387
|
+
color && /* @__PURE__ */ jsx("span", { children: labels?.[color] ?? color }),
|
|
8388
|
+
/* @__PURE__ */ jsx("span", { children: colorFormatter(model.shadeMin) }),
|
|
8389
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
8390
|
+
/* @__PURE__ */ jsx("span", { children: colorFormatter(model.shadeMax) })
|
|
8391
|
+
] }) }) : group ? /* @__PURE__ */ jsx(Legend, { categories: legendCategories, labels }) : null;
|
|
8392
|
+
return /* @__PURE__ */ jsx(
|
|
8393
|
+
ChartFrame,
|
|
8394
|
+
{
|
|
8395
|
+
...chartRootProps(rest),
|
|
8396
|
+
ref: frameRef,
|
|
8397
|
+
title,
|
|
8398
|
+
summary: resolvedSummary,
|
|
8399
|
+
isLoading: isLoading ?? false,
|
|
8400
|
+
isEmpty: rows.length === 0,
|
|
8401
|
+
emptyState,
|
|
8402
|
+
height,
|
|
8403
|
+
variant: "bar",
|
|
8404
|
+
ariaLabel: label2,
|
|
8405
|
+
className,
|
|
8406
|
+
dataTable,
|
|
8407
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
8408
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx(EChart, { option, onEvents, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } }) }),
|
|
8409
|
+
legend
|
|
8410
|
+
] })
|
|
8411
|
+
}
|
|
8412
|
+
);
|
|
8413
|
+
}
|
|
8414
|
+
var Treemap = forwardRef(TreemapImpl);
|
|
8415
|
+
Treemap.displayName = "Treemap";
|
|
8416
|
+
var TEXT23 = {
|
|
5351
8417
|
ja: {
|
|
5352
8418
|
tones: { positive: "\u826F\u597D", warning: "\u6CE8\u610F", negative: "\u8981\u5BFE\u5FDC", neutral: "\u4E2D\u7ACB" },
|
|
5353
8419
|
toneNote: (label2) => `\uFF08${label2}\uFF09`,
|
|
@@ -5381,7 +8447,7 @@ var TEXT14 = {
|
|
|
5381
8447
|
exportCsv: "Export CSV"
|
|
5382
8448
|
}
|
|
5383
8449
|
};
|
|
5384
|
-
var useText = () =>
|
|
8450
|
+
var useText = () => TEXT23[uiLanguage(useLocale())];
|
|
5385
8451
|
function resolveFormat(format, row) {
|
|
5386
8452
|
return typeof format === "function" ? format(row) : format;
|
|
5387
8453
|
}
|
|
@@ -5444,10 +8510,10 @@ function useDataTable() {
|
|
|
5444
8510
|
if (!ctx) throw new Error("useDataTable must be used inside <DataTable>");
|
|
5445
8511
|
return ctx;
|
|
5446
8512
|
}
|
|
5447
|
-
var
|
|
8513
|
+
var NO_ROWS18 = [];
|
|
5448
8514
|
function DataTable({
|
|
5449
|
-
columns =
|
|
5450
|
-
data =
|
|
8515
|
+
columns = NO_ROWS18,
|
|
8516
|
+
data = NO_ROWS18,
|
|
5451
8517
|
enableSorting = true,
|
|
5452
8518
|
sorting: sortingProp,
|
|
5453
8519
|
onSortingChange,
|
|
@@ -5474,7 +8540,7 @@ function DataTable({
|
|
|
5474
8540
|
children
|
|
5475
8541
|
}) {
|
|
5476
8542
|
const locale = useLocale();
|
|
5477
|
-
const t =
|
|
8543
|
+
const t = TEXT23[uiLanguage(locale)];
|
|
5478
8544
|
const [sorting, setSorting] = useControlledState(sortingProp, [], onSortingChange);
|
|
5479
8545
|
const [columnFilters, setColumnFilters] = useControlledState(
|
|
5480
8546
|
columnFiltersProp,
|
|
@@ -5758,19 +8824,19 @@ var DataTableContent = forwardRef(
|
|
|
5758
8824
|
}) }, headerGroup.id)) }),
|
|
5759
8825
|
/* @__PURE__ */ jsx(TableBody, { children: isLoading ? Array.from({ length: enablePagination ? table.getState().pagination.pageSize : 5 }).map(
|
|
5760
8826
|
(_, i) => /* @__PURE__ */ jsx(TableRow, { "data-slot": "data-table-skeleton-row", children: Array.from({ length: visibleColumnCount }).map((__, j) => /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }) }, j)) }, i)
|
|
5761
|
-
) : table.getRowModel().rows.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, { "data-state": row.getIsSelected() ? "selected" : void 0, children: row.getVisibleCells().map((
|
|
5762
|
-
const meta =
|
|
5763
|
-
const comparisonColumn = comparisonOf.get(
|
|
8827
|
+
) : table.getRowModel().rows.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, { "data-state": row.getIsSelected() ? "selected" : void 0, children: row.getVisibleCells().map((cell2) => {
|
|
8828
|
+
const meta = cell2.column.columnDef.meta;
|
|
8829
|
+
const comparisonColumn = comparisonOf.get(cell2.column.id);
|
|
5764
8830
|
const comparisonCell = comparisonColumn ? row.getVisibleCells().find((c) => c.column.id === comparisonColumn.id) : void 0;
|
|
5765
8831
|
return /* @__PURE__ */ jsxs(
|
|
5766
8832
|
TableCell,
|
|
5767
8833
|
{
|
|
5768
8834
|
className: columnClassName(meta, {
|
|
5769
|
-
isComparison: comparisonIds.has(
|
|
5770
|
-
isHost: comparisonOf.has(
|
|
8835
|
+
isComparison: comparisonIds.has(cell2.column.id),
|
|
8836
|
+
isHost: comparisonOf.has(cell2.column.id)
|
|
5771
8837
|
}),
|
|
5772
8838
|
children: [
|
|
5773
|
-
flexRender(
|
|
8839
|
+
flexRender(cell2.column.columnDef.cell, cell2.getContext()),
|
|
5774
8840
|
comparisonCell && /* @__PURE__ */ jsx(
|
|
5775
8841
|
"div",
|
|
5776
8842
|
{
|
|
@@ -5785,7 +8851,7 @@ var DataTableContent = forwardRef(
|
|
|
5785
8851
|
)
|
|
5786
8852
|
]
|
|
5787
8853
|
},
|
|
5788
|
-
|
|
8854
|
+
cell2.id
|
|
5789
8855
|
);
|
|
5790
8856
|
}) }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
|
|
5791
8857
|
TableCell,
|
|
@@ -5920,16 +8986,16 @@ DataTableColumnVisibility.displayName = "DataTableColumnVisibility";
|
|
|
5920
8986
|
var SIGNED_NUMBER = /^[+-]\s?[^\d\s=+\-@]{0,3}\d[\d,.]*[^\d\s=+\-@]{0,4}$/;
|
|
5921
8987
|
function toCsv(header, rows, options = {}) {
|
|
5922
8988
|
const { bom = true, guardFormulas = false } = options;
|
|
5923
|
-
const field = (
|
|
5924
|
-
let text =
|
|
5925
|
-
if (guard && typeof
|
|
8989
|
+
const field = (cell2, guard) => {
|
|
8990
|
+
let text = cell2 === null || cell2 === void 0 ? "" : String(cell2);
|
|
8991
|
+
if (guard && typeof cell2 === "string" && /^[=+\-@]/.test(text) && !SIGNED_NUMBER.test(text)) {
|
|
5926
8992
|
text = `'${text}`;
|
|
5927
8993
|
}
|
|
5928
8994
|
return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
5929
8995
|
};
|
|
5930
8996
|
const lines = [
|
|
5931
8997
|
header.map((h) => field(h, false)).join(","),
|
|
5932
|
-
...rows.map((row) => row.map((
|
|
8998
|
+
...rows.map((row) => row.map((cell2) => field(cell2, guardFormulas)).join(","))
|
|
5933
8999
|
];
|
|
5934
9000
|
return (bom ? "\uFEFF" : "") + lines.join("\r\n");
|
|
5935
9001
|
}
|
|
@@ -5983,7 +9049,7 @@ function DataTableExport({
|
|
|
5983
9049
|
disabled: isLoading,
|
|
5984
9050
|
children: [
|
|
5985
9051
|
/* @__PURE__ */ jsx(Download, { className: "size-4", "aria-hidden": "true" }),
|
|
5986
|
-
children ??
|
|
9052
|
+
children ?? TEXT23[uiLanguage(locale)].exportCsv
|
|
5987
9053
|
]
|
|
5988
9054
|
}
|
|
5989
9055
|
);
|
|
@@ -6017,8 +9083,150 @@ function DataTablePreset({
|
|
|
6017
9083
|
] });
|
|
6018
9084
|
}
|
|
6019
9085
|
DataTablePreset.displayName = "DataTablePreset";
|
|
9086
|
+
var FLOOR_SHARE = 0.2;
|
|
9087
|
+
var DEFAULT_CHART_HEIGHT3 = 288;
|
|
9088
|
+
var LIGHT_TEXT_FROM = 0.55;
|
|
9089
|
+
var dimText = (v) => v === null || v === void 0 || v === "" ? EMPTY_VALUE : String(v);
|
|
9090
|
+
var pivotCellKey = (rowKey, colKey) => `${rowKey.join("")}\0${colKey.join("")}`;
|
|
9091
|
+
function buildPivotModel(config) {
|
|
9092
|
+
const { data, rows, columns, values } = config;
|
|
9093
|
+
const rowKeys = /* @__PURE__ */ new Map();
|
|
9094
|
+
const colKeys = /* @__PURE__ */ new Map();
|
|
9095
|
+
const cells = /* @__PURE__ */ new Map();
|
|
9096
|
+
for (const row of data) {
|
|
9097
|
+
if (!row || typeof row !== "object") continue;
|
|
9098
|
+
const rk = rows.map((d) => dimText(row[d]));
|
|
9099
|
+
const ck = columns.map((d) => dimText(row[d]));
|
|
9100
|
+
const rId = rk.join("");
|
|
9101
|
+
const cId = ck.join("");
|
|
9102
|
+
if (!rowKeys.has(rId)) rowKeys.set(rId, rk);
|
|
9103
|
+
if (!colKeys.has(cId)) colKeys.set(cId, ck);
|
|
9104
|
+
cells.set(pivotCellKey(rk, ck), row);
|
|
9105
|
+
}
|
|
9106
|
+
const ranges = {};
|
|
9107
|
+
for (const value of values) {
|
|
9108
|
+
let [lo, hi] = [Infinity, -Infinity];
|
|
9109
|
+
for (const row of cells.values()) {
|
|
9110
|
+
const n = toChartNumber(row[value]);
|
|
9111
|
+
if (n !== null) [lo, hi] = [Math.min(lo, n), Math.max(hi, n)];
|
|
9112
|
+
}
|
|
9113
|
+
ranges[value] = { min: Number.isFinite(lo) ? lo : 0, max: Number.isFinite(hi) ? hi : 0 };
|
|
9114
|
+
}
|
|
9115
|
+
return {
|
|
9116
|
+
rowKeys: rowKeys.size > 0 ? [...rowKeys.values()] : [rows.map(() => EMPTY_VALUE)],
|
|
9117
|
+
colKeys: colKeys.size > 0 ? [...colKeys.values()] : [columns.map(() => EMPTY_VALUE)],
|
|
9118
|
+
cells,
|
|
9119
|
+
ranges
|
|
9120
|
+
};
|
|
9121
|
+
}
|
|
9122
|
+
function pivotSpans(keys, level) {
|
|
9123
|
+
const out = [];
|
|
9124
|
+
const prefix = (k) => k.slice(0, level + 1).join("");
|
|
9125
|
+
keys.forEach((key, i) => {
|
|
9126
|
+
const last = out[out.length - 1];
|
|
9127
|
+
if (last && prefix(keys[last.start]) === prefix(key)) last.span++;
|
|
9128
|
+
else out.push({ text: key[level] ?? "", start: i, span: 1 });
|
|
9129
|
+
});
|
|
9130
|
+
return out;
|
|
9131
|
+
}
|
|
9132
|
+
var TEXT24 = {
|
|
9133
|
+
ja: { table: "\u30D4\u30DC\u30C3\u30C8\u8868" },
|
|
9134
|
+
en: { table: "Pivot table" }
|
|
9135
|
+
};
|
|
9136
|
+
var NO_ROWS19 = [];
|
|
9137
|
+
var asList = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
9138
|
+
function PivotTable({
|
|
9139
|
+
data = NO_ROWS19,
|
|
9140
|
+
rows: rowDims,
|
|
9141
|
+
columns: colDims,
|
|
9142
|
+
values: valueKeys,
|
|
9143
|
+
formats,
|
|
9144
|
+
labels,
|
|
9145
|
+
highlight = false,
|
|
9146
|
+
tone = "chart-1",
|
|
9147
|
+
onSelect,
|
|
9148
|
+
title,
|
|
9149
|
+
summary,
|
|
9150
|
+
isLoading,
|
|
9151
|
+
emptyState,
|
|
9152
|
+
maxHeight = 480,
|
|
9153
|
+
className,
|
|
9154
|
+
ariaLabel
|
|
9155
|
+
}) {
|
|
9156
|
+
const locale = useLocale();
|
|
9157
|
+
const t = TEXT24[uiLanguage(resolveLocale(locale))];
|
|
9158
|
+
const dataRows = Array.isArray(data) ? data : NO_ROWS19;
|
|
9159
|
+
const rows = useMemo(() => asList(rowDims), [rowDims]);
|
|
9160
|
+
const columns = useMemo(() => asList(colDims), [colDims]);
|
|
9161
|
+
const values = useMemo(() => asList(valueKeys), [valueKeys]);
|
|
9162
|
+
const model = useMemo(() => buildPivotModel({ data: dataRows, rows, columns, values }), [dataRows, rows, columns, values]);
|
|
9163
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
9164
|
+
const R = rows.length;
|
|
9165
|
+
const C = columns.length;
|
|
9166
|
+
const V = values.length;
|
|
9167
|
+
const valueRow = V > 1 || C === 0;
|
|
9168
|
+
const H = C + (valueRow ? 1 : 0);
|
|
9169
|
+
const shaded = (value) => highlight === true || highlight === value;
|
|
9170
|
+
const isEmpty = dataRows.length === 0;
|
|
9171
|
+
const ready = !isLoading && !isEmpty;
|
|
9172
|
+
const cellStyle = (value, n) => {
|
|
9173
|
+
if (n === null || !shaded(value)) return void 0;
|
|
9174
|
+
const { min, max } = model.ranges[value] ?? { min: 0, max: 0 };
|
|
9175
|
+
const ratio = max > min ? Math.min(1, Math.max(0, (n - min) / (max - min))) : 1;
|
|
9176
|
+
const share = Math.round((FLOOR_SHARE + (1 - FLOOR_SHARE) * ratio) * 100);
|
|
9177
|
+
return {
|
|
9178
|
+
backgroundColor: `color-mix(in oklch, var(--${tone}) ${share}%, var(--card))`,
|
|
9179
|
+
color: ratio >= LIGHT_TEXT_FROM ? "var(--card)" : void 0
|
|
9180
|
+
};
|
|
9181
|
+
};
|
|
9182
|
+
const headerRows = Array.from({ length: H }, (_, h) => {
|
|
9183
|
+
const lead = h === H - 1 ? rows.map((d, level) => /* @__PURE__ */ jsx(TableHead, { scope: "col", className: cn(level === 0 && "pl-0"), children: label2(d) }, `dim-${d}`)) : h === 0 && R > 0 ? [/* @__PURE__ */ jsx(TableHead, { colSpan: R, rowSpan: H - 1, "aria-hidden": "true" }, "corner")] : [];
|
|
9184
|
+
const cells = h < C ? pivotSpans(model.colKeys, h).map((s) => /* @__PURE__ */ jsx(TableHead, { scope: "colgroup", colSpan: s.span * Math.max(1, V), className: "text-right", children: label2(s.text) }, `c-${h}-${s.start}`)) : model.colKeys.flatMap(
|
|
9185
|
+
(_2, ci) => values.map((v) => /* @__PURE__ */ jsx(TableHead, { scope: "col", className: "text-right", children: label2(v) }, `v-${ci}-${v}`))
|
|
9186
|
+
);
|
|
9187
|
+
return /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-transparent", children: [
|
|
9188
|
+
lead,
|
|
9189
|
+
cells
|
|
9190
|
+
] }, `h-${h}`);
|
|
9191
|
+
});
|
|
9192
|
+
const rowSpans = rows.map((_, level) => new Map(pivotSpans(model.rowKeys, level).map((s) => [s.start, s.span])));
|
|
9193
|
+
const body = model.rowKeys.map((rk, ri) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
9194
|
+
rows.map((d, level) => {
|
|
9195
|
+
const span = rowSpans[level].get(ri);
|
|
9196
|
+
if (span === void 0) return null;
|
|
9197
|
+
return /* @__PURE__ */ jsx(TableHead, { scope: span > 1 ? "rowgroup" : "row", rowSpan: span, className: cn("align-top font-medium text-foreground", level === 0 && "pl-0"), children: label2(rk[level] ?? "") }, `rh-${d}`);
|
|
9198
|
+
}),
|
|
9199
|
+
model.colKeys.flatMap(
|
|
9200
|
+
(ck, ci) => values.map((v) => {
|
|
9201
|
+
const row = model.cells.get(pivotCellKey(rk, ck));
|
|
9202
|
+
const n = row ? toChartNumber(row[v]) : null;
|
|
9203
|
+
const text = formatValue(n, formats?.[v] ?? "number", locale);
|
|
9204
|
+
return /* @__PURE__ */ jsx(TableCell, { className: "text-right tabular-nums", style: cellStyle(v, n), children: onSelect && row ? /* @__PURE__ */ jsx("button", { type: "button", className: "w-full cursor-pointer text-right tabular-nums", onClick: () => onSelect(row), children: text }) : text }, `c-${ci}-${v}`);
|
|
9205
|
+
})
|
|
9206
|
+
)
|
|
9207
|
+
] }, `r-${ri}`));
|
|
9208
|
+
return /* @__PURE__ */ jsx(
|
|
9209
|
+
ChartFrame,
|
|
9210
|
+
{
|
|
9211
|
+
title,
|
|
9212
|
+
summary,
|
|
9213
|
+
isLoading: isLoading ?? false,
|
|
9214
|
+
isEmpty,
|
|
9215
|
+
emptyState,
|
|
9216
|
+
height: ready ? "auto" : DEFAULT_CHART_HEIGHT3,
|
|
9217
|
+
variant: "list",
|
|
9218
|
+
plotRole: "group",
|
|
9219
|
+
ariaLabel: ariaLabel ?? title ?? t.table,
|
|
9220
|
+
className,
|
|
9221
|
+
children: /* @__PURE__ */ jsx("div", { "data-slot": "pivot-table", className: "relative w-full overflow-auto", style: { maxHeight }, children: /* @__PURE__ */ jsxs("table", { "data-slot": "table", "aria-label": ariaLabel ?? title ?? t.table, className: "w-full caption-bottom text-sm", children: [
|
|
9222
|
+
/* @__PURE__ */ jsx(TableHeader, { className: "sticky top-0 z-10 bg-card", children: headerRows }),
|
|
9223
|
+
/* @__PURE__ */ jsx(TableBody, { children: body })
|
|
9224
|
+
] }) })
|
|
9225
|
+
}
|
|
9226
|
+
);
|
|
9227
|
+
}
|
|
6020
9228
|
var FilterBarContext = createContext(null);
|
|
6021
|
-
var
|
|
9229
|
+
var TEXT25 = {
|
|
6022
9230
|
ja: {
|
|
6023
9231
|
filters: "\u30D5\u30A3\u30EB\u30BF\u30FC",
|
|
6024
9232
|
selected: (count) => ` (${count}\u4EF6\u9078\u629E\u4E2D)`,
|
|
@@ -6034,7 +9242,7 @@ var TEXT15 = {
|
|
|
6034
9242
|
clearAll: "Clear all filters"
|
|
6035
9243
|
}
|
|
6036
9244
|
};
|
|
6037
|
-
var useText2 = () =>
|
|
9245
|
+
var useText2 = () => TEXT25[uiLanguage(useLocale())];
|
|
6038
9246
|
function useFilterBar() {
|
|
6039
9247
|
const ctx = useContext(FilterBarContext);
|
|
6040
9248
|
if (!ctx) throw new Error("useFilterBar must be used within <FilterBar>");
|
|
@@ -6337,7 +9545,7 @@ var PRESET_LABELS = {
|
|
|
6337
9545
|
lastFiscalYear: "Last fiscal year"
|
|
6338
9546
|
}
|
|
6339
9547
|
};
|
|
6340
|
-
var
|
|
9548
|
+
var TEXT26 = {
|
|
6341
9549
|
ja: { placeholder: "\u671F\u9593\u3092\u9078\u629E", rangeSeparator: " - " },
|
|
6342
9550
|
en: { placeholder: "Pick a date range", rangeSeparator: " \u2013 " }
|
|
6343
9551
|
};
|
|
@@ -6355,7 +9563,7 @@ function createDatePresets({
|
|
|
6355
9563
|
const today = now();
|
|
6356
9564
|
return { from: startOfDay(subDays(today, days - 1)), to: endOfDay(today) };
|
|
6357
9565
|
};
|
|
6358
|
-
const
|
|
9566
|
+
const toDate3 = (unit) => () => {
|
|
6359
9567
|
const today = now();
|
|
6360
9568
|
return {
|
|
6361
9569
|
from: fiscalPeriodRange(today, { unit, fiscalYearStartMonth: startMonth }).from,
|
|
@@ -6405,9 +9613,9 @@ function createDatePresets({
|
|
|
6405
9613
|
return { from: startOfMonth(lastMonth), to: endOfMonth(lastMonth) };
|
|
6406
9614
|
}
|
|
6407
9615
|
},
|
|
6408
|
-
{ label: t.thisQuarter, getValue:
|
|
9616
|
+
{ label: t.thisQuarter, getValue: toDate3("quarter") },
|
|
6409
9617
|
{ label: t.lastQuarter, getValue: previous("quarter") },
|
|
6410
|
-
{ label: t.thisFiscalYear, getValue:
|
|
9618
|
+
{ label: t.thisFiscalYear, getValue: toDate3("year") },
|
|
6411
9619
|
{ label: t.lastFiscalYear, getValue: previous("year") }
|
|
6412
9620
|
];
|
|
6413
9621
|
}
|
|
@@ -6438,7 +9646,7 @@ var DateRangePicker = forwardRef(
|
|
|
6438
9646
|
const [open, setOpen] = useState(false);
|
|
6439
9647
|
const isMobile = useIsMobile();
|
|
6440
9648
|
const locale = useLocale(localeProp);
|
|
6441
|
-
const t =
|
|
9649
|
+
const t = TEXT26[uiLanguage(locale)];
|
|
6442
9650
|
const presets = useMemo(
|
|
6443
9651
|
() => presetsProp ?? createDatePresets({ locale, fiscalYearStartMonth }),
|
|
6444
9652
|
[presetsProp, locale, fiscalYearStartMonth]
|
|
@@ -6655,7 +9863,7 @@ function RemoveButton({ label: label2, onRemove, iconClassName }) {
|
|
|
6655
9863
|
}
|
|
6656
9864
|
);
|
|
6657
9865
|
}
|
|
6658
|
-
var
|
|
9866
|
+
var TEXT27 = {
|
|
6659
9867
|
ja: { placeholder: "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044", search: "\u691C\u7D22...", empty: "\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F", clear: "\u30AF\u30EA\u30A2" },
|
|
6660
9868
|
en: { placeholder: "Select an option", search: "Search...", empty: "No results found.", clear: "Clear" }
|
|
6661
9869
|
};
|
|
@@ -6675,7 +9883,7 @@ var SearchableSelect = forwardRef(
|
|
|
6675
9883
|
className
|
|
6676
9884
|
}, ref) {
|
|
6677
9885
|
const [open, setOpen] = useState(false);
|
|
6678
|
-
const t =
|
|
9886
|
+
const t = TEXT27[uiLanguage(useLocale())];
|
|
6679
9887
|
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
6680
9888
|
const selectedOption = useMemo(
|
|
6681
9889
|
() => allOptions.find((opt) => opt.value === value),
|
|
@@ -6728,7 +9936,7 @@ var SearchableSelect = forwardRef(
|
|
|
6728
9936
|
}
|
|
6729
9937
|
);
|
|
6730
9938
|
SearchableSelect.displayName = "SearchableSelect";
|
|
6731
|
-
var
|
|
9939
|
+
var TEXT28 = {
|
|
6732
9940
|
ja: {
|
|
6733
9941
|
placeholder: "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
6734
9942
|
search: "\u691C\u7D22...",
|
|
@@ -6745,10 +9953,10 @@ var TEXT18 = {
|
|
|
6745
9953
|
}
|
|
6746
9954
|
};
|
|
6747
9955
|
var NO_OPTIONS3 = [];
|
|
6748
|
-
var
|
|
9956
|
+
var NO_VALUES2 = [];
|
|
6749
9957
|
var MultiSelect = forwardRef(function MultiSelect2({
|
|
6750
9958
|
options = NO_OPTIONS3,
|
|
6751
|
-
value =
|
|
9959
|
+
value = NO_VALUES2,
|
|
6752
9960
|
onChange,
|
|
6753
9961
|
onSearch,
|
|
6754
9962
|
maxCount = 3,
|
|
@@ -6761,7 +9969,7 @@ var MultiSelect = forwardRef(function MultiSelect2({
|
|
|
6761
9969
|
}, ref) {
|
|
6762
9970
|
const [open, setOpen] = useState(false);
|
|
6763
9971
|
const listId = useId();
|
|
6764
|
-
const t =
|
|
9972
|
+
const t = TEXT28[uiLanguage(useLocale())];
|
|
6765
9973
|
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
6766
9974
|
const selectedOptions = useMemo(
|
|
6767
9975
|
() => allOptions.filter((opt) => value.includes(opt.value)),
|
|
@@ -6891,10 +10099,10 @@ function computeTargetProgress(current, target, options) {
|
|
|
6891
10099
|
elapsed
|
|
6892
10100
|
};
|
|
6893
10101
|
}
|
|
6894
|
-
var
|
|
10102
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6895
10103
|
function inferPeriodGrain(period) {
|
|
6896
10104
|
if (!period) return void 0;
|
|
6897
|
-
const days = (toTime(period.end) - toTime(period.start)) /
|
|
10105
|
+
const days = (toTime(period.end) - toTime(period.start)) / DAY_MS2;
|
|
6898
10106
|
if (!Number.isFinite(days)) return void 0;
|
|
6899
10107
|
const near = (min, max) => days >= min - 1 / 24 && days <= max + 1 / 24;
|
|
6900
10108
|
if (near(1, 1)) return "day";
|
|
@@ -7043,7 +10251,7 @@ var LONG_VALUE_CHARS = 10;
|
|
|
7043
10251
|
var DELTA_UNDER_PLACEMENT = "col-start-1 row-span-1 row-start-3 justify-self-start";
|
|
7044
10252
|
var KPI_FOOTER_CLASS = "flex-col items-start gap-2 rounded-none border-t-0 bg-transparent pt-0 text-sm";
|
|
7045
10253
|
var PLACEHOLDER = /\{(\w+)\}/g;
|
|
7046
|
-
var
|
|
10254
|
+
var TEXT29 = {
|
|
7047
10255
|
ja: {
|
|
7048
10256
|
target: "\u76EE\u6A19",
|
|
7049
10257
|
provisional: "\u901F\u5831",
|
|
@@ -7175,7 +10383,7 @@ function KpiCard({
|
|
|
7175
10383
|
...props
|
|
7176
10384
|
}) {
|
|
7177
10385
|
const locale = useLocale(localeProp);
|
|
7178
|
-
const t =
|
|
10386
|
+
const t = TEXT29[uiLanguage(locale)];
|
|
7179
10387
|
const allComparisons = [...comparisonProp ? [comparisonProp] : [], ...comparisons ?? []];
|
|
7180
10388
|
const comparison = allComparisons[0];
|
|
7181
10389
|
const isProvisional = provisional !== void 0 && provisional !== false;
|
|
@@ -7426,7 +10634,7 @@ function updatedAtText(updatedAt, display, locale, t, now, timeZone) {
|
|
|
7426
10634
|
const absolute2 = formatDate(updatedAt, "datetime", locale, { timeZone });
|
|
7427
10635
|
return display === "absolute" ? t.updatedAbsolute(absolute2) : t.updatedBoth(absolute2, relative2);
|
|
7428
10636
|
}
|
|
7429
|
-
var
|
|
10637
|
+
var TEXT30 = {
|
|
7430
10638
|
ja: {
|
|
7431
10639
|
flat: (value) => `\u5909\u5316\u306A\u3057 ${value}%`,
|
|
7432
10640
|
up: (value) => `${value}% \u5897\u52A0`,
|
|
@@ -7455,7 +10663,7 @@ var TrendIndicator = forwardRef(
|
|
|
7455
10663
|
const isGood = direction === "neutral" ? null : direction === "up" === positiveIsGood;
|
|
7456
10664
|
const sentiment = isGood === null ? "neutral" : isGood ? "positive" : "negative";
|
|
7457
10665
|
const Icon = direction === "up" ? ArrowUp : direction === "down" ? ArrowDown : Minus;
|
|
7458
|
-
const t =
|
|
10666
|
+
const t = TEXT30[uiLanguage(useLocale())];
|
|
7459
10667
|
const ariaLabel = direction === "neutral" ? t.flat(value) : direction === "up" ? t.up(value) : t.down(value);
|
|
7460
10668
|
return /* @__PURE__ */ jsxs(
|
|
7461
10669
|
"span",
|
|
@@ -7476,93 +10684,6 @@ var TrendIndicator = forwardRef(
|
|
|
7476
10684
|
}
|
|
7477
10685
|
);
|
|
7478
10686
|
TrendIndicator.displayName = "TrendIndicator";
|
|
7479
|
-
var STATUS_LABELS = {
|
|
7480
|
-
ja: {
|
|
7481
|
-
success: "\u6B63\u5E38",
|
|
7482
|
-
partial: "\u4E00\u90E8\u5931\u6557",
|
|
7483
|
-
failed: "\u5931\u6557",
|
|
7484
|
-
delayed: "\u9045\u5EF6",
|
|
7485
|
-
running: "\u5B9F\u884C\u4E2D",
|
|
7486
|
-
stopped: "\u505C\u6B62\u4E2D",
|
|
7487
|
-
active: "\u6709\u52B9",
|
|
7488
|
-
inactive: "\u7121\u52B9",
|
|
7489
|
-
warning: "\u8B66\u544A",
|
|
7490
|
-
error: "\u30A8\u30E9\u30FC",
|
|
7491
|
-
pending: "\u4FDD\u7559\u4E2D"
|
|
7492
|
-
},
|
|
7493
|
-
en: {
|
|
7494
|
-
success: "Healthy",
|
|
7495
|
-
partial: "Partially failed",
|
|
7496
|
-
failed: "Failed",
|
|
7497
|
-
delayed: "Delayed",
|
|
7498
|
-
running: "Running",
|
|
7499
|
-
stopped: "Stopped",
|
|
7500
|
-
active: "Active",
|
|
7501
|
-
inactive: "Inactive",
|
|
7502
|
-
warning: "Warning",
|
|
7503
|
-
error: "Error",
|
|
7504
|
-
pending: "Pending"
|
|
7505
|
-
}
|
|
7506
|
-
};
|
|
7507
|
-
var STATUS_VOCABULARY = {
|
|
7508
|
-
success: { label: STATUS_LABELS.en.success, tone: "positive" },
|
|
7509
|
-
partial: { label: STATUS_LABELS.en.partial, tone: "warning" },
|
|
7510
|
-
failed: { label: STATUS_LABELS.en.failed, tone: "negative" },
|
|
7511
|
-
delayed: { label: STATUS_LABELS.en.delayed, tone: "warning" },
|
|
7512
|
-
running: { label: STATUS_LABELS.en.running, tone: "info" },
|
|
7513
|
-
stopped: { label: STATUS_LABELS.en.stopped, tone: "neutral" },
|
|
7514
|
-
active: { label: STATUS_LABELS.en.active, tone: "positive" },
|
|
7515
|
-
inactive: { label: STATUS_LABELS.en.inactive, tone: "neutral" },
|
|
7516
|
-
warning: { label: STATUS_LABELS.en.warning, tone: "warning" },
|
|
7517
|
-
error: { label: STATUS_LABELS.en.error, tone: "negative" },
|
|
7518
|
-
pending: { label: STATUS_LABELS.en.pending, tone: "info" }
|
|
7519
|
-
};
|
|
7520
|
-
function statusLabel(status, locale) {
|
|
7521
|
-
return Object.hasOwn(STATUS_VOCABULARY, status) ? STATUS_LABELS[uiLanguage(resolveLocale(locale))][status] : status;
|
|
7522
|
-
}
|
|
7523
|
-
var defaultColorMap = Object.fromEntries(
|
|
7524
|
-
Object.entries(STATUS_VOCABULARY).map(([key, { tone }]) => [key, tone])
|
|
7525
|
-
);
|
|
7526
|
-
var legacyColorTones = {
|
|
7527
|
-
green: "positive",
|
|
7528
|
-
gray: "neutral",
|
|
7529
|
-
yellow: "warning",
|
|
7530
|
-
red: "negative",
|
|
7531
|
-
blue: "info"
|
|
7532
|
-
};
|
|
7533
|
-
var toneStyles = {
|
|
7534
|
-
positive: { dot: "bg-positive", bg: "bg-positive-subtle", text: "text-positive", border: "border-positive/20 dark:border-positive/25" },
|
|
7535
|
-
negative: { dot: "bg-negative", bg: "bg-negative-subtle", text: "text-negative", border: "border-negative/20 dark:border-negative/25" },
|
|
7536
|
-
warning: { dot: "bg-warning", bg: "bg-warning-subtle", text: "text-warning", border: "border-warning/20 dark:border-warning/25" },
|
|
7537
|
-
info: { dot: "bg-info", bg: "bg-info-subtle", text: "text-info", border: "border-info/20 dark:border-info/25" },
|
|
7538
|
-
neutral: { dot: "bg-neutral", bg: "bg-neutral-subtle", text: "text-neutral", border: "border-neutral/20 dark:border-neutral/25" }
|
|
7539
|
-
};
|
|
7540
|
-
var resolveTone = (color) => {
|
|
7541
|
-
if (color === void 0) return "neutral";
|
|
7542
|
-
if (Object.hasOwn(toneStyles, color)) return color;
|
|
7543
|
-
return legacyColorTones[color] ?? "neutral";
|
|
7544
|
-
};
|
|
7545
|
-
var StatusBadge = forwardRef(function StatusBadge2({ status, label: label2, colorMap, className }, ref) {
|
|
7546
|
-
const locale = useLocale();
|
|
7547
|
-
const mergedColorMap = { ...defaultColorMap, ...colorMap };
|
|
7548
|
-
const style = toneStyles[resolveTone(mergedColorMap[status])];
|
|
7549
|
-
const displayLabel = label2 ?? statusLabel(status, locale);
|
|
7550
|
-
return /* @__PURE__ */ jsxs(
|
|
7551
|
-
Badge,
|
|
7552
|
-
{
|
|
7553
|
-
ref,
|
|
7554
|
-
"data-slot": "status-badge",
|
|
7555
|
-
variant: "outline",
|
|
7556
|
-
className: cn("gap-1.5 rounded-md", style.border, style.bg, style.text, className),
|
|
7557
|
-
"aria-label": displayLabel,
|
|
7558
|
-
children: [
|
|
7559
|
-
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", style.dot), "aria-hidden": "true" }),
|
|
7560
|
-
displayLabel
|
|
7561
|
-
]
|
|
7562
|
-
}
|
|
7563
|
-
);
|
|
7564
|
-
});
|
|
7565
|
-
StatusBadge.displayName = "StatusBadge";
|
|
7566
10687
|
var Placeholder = forwardRef(function Placeholder2({ className, ...props }, ref) {
|
|
7567
10688
|
return /* @__PURE__ */ jsx(
|
|
7568
10689
|
"span",
|
|
@@ -7575,7 +10696,69 @@ var Placeholder = forwardRef(function Placeholder2({ className, ...props }, ref)
|
|
|
7575
10696
|
);
|
|
7576
10697
|
});
|
|
7577
10698
|
Placeholder.displayName = "Placeholder";
|
|
10699
|
+
function DraftChart({ children }) {
|
|
10700
|
+
if (!import.meta.env.DEV) return null;
|
|
10701
|
+
return /* @__PURE__ */ jsx(DraftChartPreview, { children });
|
|
10702
|
+
}
|
|
10703
|
+
var STYLE_ID = "vantage-draft-chart";
|
|
10704
|
+
var DRAFT_CSS = `@layer components {
|
|
10705
|
+
[data-sq-draft] {
|
|
10706
|
+
position: relative;
|
|
10707
|
+
isolation: isolate;
|
|
10708
|
+
}
|
|
10709
|
+
[data-sq-draft]::after {
|
|
10710
|
+
content: "";
|
|
10711
|
+
position: absolute;
|
|
10712
|
+
inset: -1px;
|
|
10713
|
+
z-index: 2147483647;
|
|
10714
|
+
border-radius: inherit;
|
|
10715
|
+
background-color: var(--background);
|
|
10716
|
+
opacity: 0.75;
|
|
10717
|
+
pointer-events: none;
|
|
10718
|
+
transition: opacity 200ms ease;
|
|
10719
|
+
}
|
|
10720
|
+
[data-sq-draft]:hover::after {
|
|
10721
|
+
opacity: 0;
|
|
10722
|
+
}
|
|
10723
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10724
|
+
[data-sq-draft]::after {
|
|
10725
|
+
transition: none;
|
|
10726
|
+
}
|
|
10727
|
+
}
|
|
10728
|
+
}`;
|
|
10729
|
+
function DraftChartPreview({ children }) {
|
|
10730
|
+
const ref = useRef(null);
|
|
10731
|
+
useInsertionEffect(() => {
|
|
10732
|
+
let style = document.getElementById(STYLE_ID);
|
|
10733
|
+
if (!style) {
|
|
10734
|
+
style = document.createElement("style");
|
|
10735
|
+
style.id = STYLE_ID;
|
|
10736
|
+
document.head.appendChild(style);
|
|
10737
|
+
}
|
|
10738
|
+
if (style.textContent !== DRAFT_CSS) style.textContent = DRAFT_CSS;
|
|
10739
|
+
}, []);
|
|
10740
|
+
useLayoutEffect(() => {
|
|
10741
|
+
const wrapper = ref.current;
|
|
10742
|
+
if (!wrapper) return;
|
|
10743
|
+
let marked = null;
|
|
10744
|
+
const mark = () => {
|
|
10745
|
+
const root = wrapper.firstElementChild;
|
|
10746
|
+
if (root === marked) return;
|
|
10747
|
+
marked?.removeAttribute("data-sq-draft");
|
|
10748
|
+
root?.setAttribute("data-sq-draft", "");
|
|
10749
|
+
marked = root;
|
|
10750
|
+
};
|
|
10751
|
+
mark();
|
|
10752
|
+
const observer = new MutationObserver(mark);
|
|
10753
|
+
observer.observe(wrapper, { childList: true });
|
|
10754
|
+
return () => {
|
|
10755
|
+
observer.disconnect();
|
|
10756
|
+
marked?.removeAttribute("data-sq-draft");
|
|
10757
|
+
};
|
|
10758
|
+
}, []);
|
|
10759
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "draft-chart", style: { display: "contents" }, children });
|
|
10760
|
+
}
|
|
7578
10761
|
|
|
7579
|
-
export { AppShell, AreaChart, BarChart, BarList, COMPARISON_LABELS, CategoryBar, ChartFrame, DEFAULT_SAMPLE_LABEL, DELTA_NOT_COMPARABLE, DashboardCard, DashboardCardAction, DashboardCardContent, DashboardCardDescription, DashboardCardFooter, DashboardCardHeader, DashboardCardPreset, DashboardCardSkeleton, DashboardCardTitle, DataTable, DataTableColumnVisibility, DataTableContent, DataTableExport, DataTablePagination, DataTablePreset, DataTableToolbar, DateRangePicker, Delta, DonutChart, FilterBar, FilterBarActiveChips, FilterBarClearButton, FilterBarMultiSelect, FilterBarPreset, FilterBarSelect, FunnelSteps, Heatmap, KpiCard, Legend, LineChart, MetricUnit, MetricValue, MultiSelect, PageShell, PageShellActions, PageShellContent, PageShellDescription, PageShellHeader, PageShellHeaderEnd, PageShellHeading, PageShellMeta, PageShellSummary, PageShellSummaryCard, PageShellTitle, Placeholder, ProgressCircle, RECORD_MIN_POINTS, SampleBadge, SampleClaim, SampleData, ScatterChart, SearchableSelect, SectionHeader, SegmentedControl, StatusBadge, Tracker, TrendIndicator, UpdatedAt, WaterfallChart, comparisonBaseline, comparisonLabel, computeComparison, computeTargetProgress, defaultChangeDisplay, defaultPresets, deltaVariants, detectRecord, fillKpiTemplate, flattenOptions, formatChange, formatChangeAmount, formatRelativeTime, funnelStepsVariants, inferPeriodGrain, isGroupedOptions, metricValueVariants, pageShellSummaryCardVariants, tableToCsv, toCsv, toneForValue, useDataTable, useSampleMark };
|
|
7580
|
-
//# sourceMappingURL=chunk-
|
|
7581
|
-
//# sourceMappingURL=chunk-
|
|
10762
|
+
export { AppShell, AreaChart, AttentionList, BarChart, BarList, BoxPlotChart, COMPARISON_LABELS, CategoryBar, ChartFrame, ComboChart, DEFAULT_SAMPLE_LABEL, DELTA_NOT_COMPARABLE, DashboardCard, DashboardCardAction, DashboardCardContent, DashboardCardDescription, DashboardCardFooter, DashboardCardHeader, DashboardCardPreset, DashboardCardSkeleton, DashboardCardTitle, DataTable, DataTableColumnVisibility, DataTableContent, DataTableExport, DataTablePagination, DataTablePreset, DataTableToolbar, DateRangePicker, Delta, DonutChart, DraftChart, FilledMap, FilterBar, FilterBarActiveChips, FilterBarClearButton, FilterBarMultiSelect, FilterBarPreset, FilterBarSelect, ForecastChart, FunnelSteps, Heatmap, HistogramChart, KpiCard, Legend, LineChart, MetricUnit, MetricValue, MultiSelect, PageShell, PageShellActions, PageShellContent, PageShellDescription, PageShellHeader, PageShellHeaderEnd, PageShellHeading, PageShellMeta, PageShellSummary, PageShellSummaryCard, PageShellTitle, PairedChart, PivotTable, Placeholder, ProgressCircle, RECORD_MIN_POINTS, SampleBadge, SampleClaim, SampleData, ScatterChart, SearchableSelect, SectionHeader, SegmentedControl, StatusBadge, SymbolMap, Tracker, Treemap, TrendIndicator, UpdatedAt, WaterfallChart, comparisonBaseline, comparisonLabel, computeComparison, computeTargetProgress, defaultChangeDisplay, defaultPresets, deltaVariants, detectRecord, fillKpiTemplate, flattenOptions, formatChange, formatChangeAmount, formatRelativeTime, funnelStepsVariants, inferPeriodGrain, isGroupedOptions, metricValueVariants, pageShellSummaryCardVariants, tableToCsv, toCsv, toneForValue, useDataTable, useSampleMark };
|
|
10763
|
+
//# sourceMappingURL=chunk-YJQCB3UP.js.map
|
|
10764
|
+
//# sourceMappingURL=chunk-YJQCB3UP.js.map
|