@squadbase/vantage 0.7.0 → 0.7.1-dev1.0d13f9a
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-QNOX7WLZ.js → chunk-5HBUBVQ3.js} +94 -8
- package/dist/chunk-5HBUBVQ3.js.map +1 -0
- package/dist/{chunk-EG4PX726.js → chunk-GWXKENNH.js} +122 -5
- package/dist/chunk-GWXKENNH.js.map +1 -0
- package/dist/{chunk-77CISBMG.js → chunk-I2GWIPTD.js} +3685 -350
- package/dist/chunk-I2GWIPTD.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-2IAPIOPS.js → chunk-U6MWBCM6.js} +28 -7
- package/dist/chunk-U6MWBCM6.js.map +1 -0
- package/dist/{chunk-3RJEOP3J.js → chunk-XRKSIC5Z.js} +5 -3
- package/dist/chunk-XRKSIC5Z.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/components/index.d.ts +849 -12
- package/dist/components/index.js +5 -5
- 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 +4 -4
- package/dist/table/index.js +5 -6
- package/dist/table/index.js.map +1 -1
- package/dist/ui/index.d.ts +35 -4
- package/dist/ui/index.js +174 -11
- 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 +5319 -0
- package/registry/ui/area-chart.tsx +43 -8
- package/registry/ui/bar-chart.tsx +43 -8
- package/registry/ui/bar-list.tsx +43 -8
- package/registry/ui/box-plot-chart.tsx +42 -7
- package/registry/ui/category-bar.tsx +25 -5
- package/registry/ui/chart-frame.tsx +55 -14
- package/registry/ui/checkbox.tsx +4 -3
- package/registry/ui/code-block.tsx +44 -0
- package/registry/ui/combo-chart.tsx +1869 -0
- package/registry/ui/combobox.tsx +7 -0
- package/registry/ui/context-menu.tsx +71 -0
- package/registry/ui/dashboard-card.tsx +23 -10
- package/registry/ui/data-table.tsx +10 -2
- package/registry/ui/date-range-picker.tsx +51 -4
- package/registry/ui/delta.tsx +39 -12
- package/registry/ui/donut-chart.tsx +43 -8
- package/registry/ui/echart.tsx +167 -7
- package/registry/ui/file-dropzone.tsx +143 -0
- package/registry/ui/filter-bar.tsx +77 -2
- package/registry/ui/forecast-chart.tsx +4769 -0
- package/registry/ui/funnel-steps.tsx +23 -5
- package/registry/ui/heatmap.tsx +20 -3
- package/registry/ui/histogram-chart.tsx +42 -7
- package/registry/ui/kpi-card.tsx +15 -9
- package/registry/ui/legend.tsx +73 -15
- package/registry/ui/line-chart.tsx +43 -8
- package/registry/ui/metric-value.tsx +2 -2
- package/registry/ui/page-shell.tsx +27 -4
- package/registry/ui/paired-chart.tsx +44 -9
- package/registry/ui/pivot-table.tsx +1260 -0
- package/registry/ui/progress-circle.tsx +2 -2
- package/registry/ui/progress.tsx +1 -1
- package/registry/ui/scatter-chart.tsx +50 -12
- package/registry/ui/sparkline.tsx +44 -9
- package/registry/ui/states.tsx +34 -1
- package/registry/ui/status-badge.tsx +7 -7
- package/registry/ui/tabs.tsx +1 -0
- package/registry/ui/treemap.tsx +1934 -0
- package/registry/ui/waterfall-chart.tsx +64 -11
- package/templates/AGENTS.md +3 -3
- package/templates/agents/page.md +5 -4
- 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-EG4PX726.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-GWXKENNH.js';
|
|
2
|
+
import { Sparkline } from './chunk-U6MWBCM6.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, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, Card, CardHeader, CardDescription, CardTitle, CardAction, CardFooter } from './chunk-5HBUBVQ3.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,
|
|
7
|
+
import { forwardRef, useState, useEffect, createContext, useId, useCallback, useMemo, useRef, 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
|
-
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';
|
|
10
|
+
import { Search, Columns3, CalendarIcon, ChevronDownIcon, ArrowUp, ArrowDown, ArrowRight, Minus, ChevronRight, ArrowUpDown, XIcon, LoaderIcon, CheckIcon, Download, ChevronDown, Plus, X, CircleX, TriangleAlert, CircleCheck } from 'lucide-react';
|
|
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, subYears, differenceInCalendarDays, addMonths, 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: " };
|
|
@@ -621,7 +627,7 @@ var DashboardCardFooter = forwardRef(
|
|
|
621
627
|
{
|
|
622
628
|
ref,
|
|
623
629
|
"data-slot": "dashboard-card-footer",
|
|
624
|
-
className: cn("flex items-center
|
|
630
|
+
className: cn("-mx-1 -mb-1 flex min-w-0 items-center justify-between gap-3 border-t px-4 py-2.5 text-xs text-muted-foreground tabular-nums group-data-[size=sm]:px-3", className),
|
|
625
631
|
...props
|
|
626
632
|
}
|
|
627
633
|
)
|
|
@@ -647,6 +653,7 @@ function DashboardCardSkeleton({ className }) {
|
|
|
647
653
|
function DashboardCardPreset({
|
|
648
654
|
title,
|
|
649
655
|
description,
|
|
656
|
+
headline,
|
|
650
657
|
actions,
|
|
651
658
|
footer,
|
|
652
659
|
children,
|
|
@@ -658,15 +665,21 @@ function DashboardCardPreset({
|
|
|
658
665
|
...props
|
|
659
666
|
}) {
|
|
660
667
|
const sample = useSampleMark();
|
|
661
|
-
const
|
|
662
|
-
|
|
668
|
+
const hasTitle = Boolean(title || description);
|
|
669
|
+
const floatingMark = sample && !hasTitle && !headline && !actions;
|
|
670
|
+
const hasHeader = hasTitle || headline || actions || sample && !floatingMark;
|
|
671
|
+
return /* @__PURE__ */ jsx(DashboardCard, { className: cn(floatingMark && "relative", className), subgrid, ...props, children: /* @__PURE__ */ jsxs(SampleClaim, { children: [
|
|
672
|
+
floatingMark && /* @__PURE__ */ jsx(SampleBadge, { label: sample.label, className: "absolute top-2 right-2 z-10" }),
|
|
663
673
|
hasHeader && /* @__PURE__ */ jsxs(DashboardCardHeader, { className: cn(subgrid && "row-start-1", headerClassName), children: [
|
|
664
|
-
(
|
|
665
|
-
|
|
674
|
+
(hasTitle || sample) && /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
675
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-1", children: [
|
|
676
|
+
title && /* @__PURE__ */ jsx(DashboardCardTitle, { children: title }),
|
|
677
|
+
sample && /* @__PURE__ */ jsx(SampleBadge, { label: sample.label })
|
|
678
|
+
] }),
|
|
666
679
|
description && /* @__PURE__ */ jsx(DashboardCardDescription, { children: description })
|
|
667
680
|
] }),
|
|
668
|
-
(
|
|
669
|
-
|
|
681
|
+
(headline || actions) && /* @__PURE__ */ jsxs(DashboardCardAction, { className: "flex items-center gap-2", children: [
|
|
682
|
+
headline && /* @__PURE__ */ jsx("div", { "data-slot": "dashboard-card-headline", className: "text-sm tabular-nums text-muted-foreground", children: headline }),
|
|
670
683
|
actions
|
|
671
684
|
] })
|
|
672
685
|
] }),
|
|
@@ -690,6 +703,7 @@ var ChartFrame = forwardRef(function ChartFrame2({
|
|
|
690
703
|
ariaLabel,
|
|
691
704
|
plotRole = "img",
|
|
692
705
|
dataTable,
|
|
706
|
+
footnote,
|
|
693
707
|
className,
|
|
694
708
|
children,
|
|
695
709
|
...props
|
|
@@ -699,6 +713,17 @@ var ChartFrame = forwardRef(function ChartFrame2({
|
|
|
699
713
|
const titleId = title ? `${id}-title` : void 0;
|
|
700
714
|
const summaryId = summary ? `${id}-summary` : void 0;
|
|
701
715
|
const state = isLoading ? "loading" : isEmpty ? "empty" : "ready";
|
|
716
|
+
const [claims, setClaims] = useState(0);
|
|
717
|
+
const claim = useCallback(() => {
|
|
718
|
+
setClaims((n) => n + 1);
|
|
719
|
+
return () => setClaims((n) => n - 1);
|
|
720
|
+
}, []);
|
|
721
|
+
const claimed = claims > 0 && plotRole === "img";
|
|
722
|
+
const plotOwnsImage = state === "ready" && claimed;
|
|
723
|
+
const plot = useMemo(
|
|
724
|
+
() => ({ image: plotRole === "img", claimed, claim, ariaLabel: titleId ? void 0 : ariaLabel, ariaLabelledBy: titleId, ariaDescribedBy: summaryId }),
|
|
725
|
+
[plotRole, claimed, claim, titleId, ariaLabel, summaryId]
|
|
726
|
+
);
|
|
702
727
|
return /* @__PURE__ */ jsxs("figure", { "data-slot": "chart-frame", className: cn("m-0 flex w-full min-w-0 flex-col gap-3", className), ...props, children: [
|
|
703
728
|
(title || summary) && /* @__PURE__ */ jsxs("figcaption", { "data-slot": "chart-frame-header", className: "flex flex-col gap-1", children: [
|
|
704
729
|
title && /* @__PURE__ */ jsx("h3", { id: titleId, "data-slot": "chart-frame-title", className: "text-sm leading-snug font-semibold", children: title }),
|
|
@@ -710,16 +735,17 @@ var ChartFrame = forwardRef(function ChartFrame2({
|
|
|
710
735
|
ref,
|
|
711
736
|
"data-slot": "chart-frame-body",
|
|
712
737
|
"data-state": state,
|
|
713
|
-
role: state === "ready" ? plotRole : "group",
|
|
714
|
-
"aria-label": titleId ? void 0 : ariaLabel,
|
|
715
|
-
"aria-labelledby": titleId,
|
|
716
|
-
"aria-describedby": summaryId,
|
|
738
|
+
role: plotOwnsImage ? void 0 : state === "ready" ? plotRole : "group",
|
|
739
|
+
"aria-label": plotOwnsImage || titleId ? void 0 : ariaLabel,
|
|
740
|
+
"aria-labelledby": plotOwnsImage ? void 0 : titleId,
|
|
741
|
+
"aria-describedby": plotOwnsImage ? void 0 : summaryId,
|
|
717
742
|
"aria-busy": isLoading || void 0,
|
|
718
743
|
className: "relative w-full min-w-0",
|
|
719
744
|
style: { height: typeof height === "number" ? `${height}px` : height },
|
|
720
|
-
children: state === "loading" ? /* @__PURE__ */ jsx(ChartSkeleton, { variant, loadingText: t.loading }) : state === "empty" ? /* @__PURE__ */ jsx(ChartEmpty, { ...emptyState, defaultTitle: t.emptyTitle }) : /* @__PURE__ */ jsx(ChartFrameContext.Provider, { value:
|
|
745
|
+
children: state === "loading" ? /* @__PURE__ */ jsx(ChartSkeleton, { variant, loadingText: t.loading }) : state === "empty" ? /* @__PURE__ */ jsx(ChartEmpty, { ...emptyState, defaultTitle: t.emptyTitle }) : /* @__PURE__ */ jsx(ChartFrameContext.Provider, { value: plot, children })
|
|
721
746
|
}
|
|
722
747
|
),
|
|
748
|
+
footnote && /* @__PURE__ */ jsx("p", { "data-slot": "chart-frame-footnote", className: "text-xs leading-snug text-muted-foreground", children: footnote }),
|
|
723
749
|
state === "ready" && dataTable && dataTable.rows.length > 0 && /* @__PURE__ */ jsx(ChartDataTableView, { table: dataTable, fallbackCaption: title ?? ariaLabel })
|
|
724
750
|
] });
|
|
725
751
|
});
|
|
@@ -743,7 +769,7 @@ function ChartDataTableView({ table, fallbackCaption }) {
|
|
|
743
769
|
caption && /* @__PURE__ */ jsx("caption", { children: caption }),
|
|
744
770
|
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: table.columns.map((column, i) => /* @__PURE__ */ jsx("th", { scope: "col", children: column }, i)) }) }),
|
|
745
771
|
/* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, r) => /* @__PURE__ */ jsx("tr", { children: row.map(
|
|
746
|
-
(
|
|
772
|
+
(cell2, c) => c === 0 ? /* @__PURE__ */ jsx("th", { scope: "row", children: cell2 }, c) : /* @__PURE__ */ jsx("td", { children: cell2 }, c)
|
|
747
773
|
) }, r)) })
|
|
748
774
|
] }) });
|
|
749
775
|
}
|
|
@@ -800,8 +826,8 @@ function chartColorAt(colors, i) {
|
|
|
800
826
|
const palette = colors && colors.length > 0 ? colors : DEFAULT_CHART_COLORS;
|
|
801
827
|
return palette[i % palette.length];
|
|
802
828
|
}
|
|
803
|
-
function tintCss(cssColor,
|
|
804
|
-
return `color-mix(in oklab, ${cssColor} ${
|
|
829
|
+
function tintCss(cssColor, percent2) {
|
|
830
|
+
return `color-mix(in oklab, ${cssColor} ${percent2}%, transparent)`;
|
|
805
831
|
}
|
|
806
832
|
function resolveValueFormatter(valueFormatter, format, locale) {
|
|
807
833
|
return valueFormatter ?? toValueFormatter(format, locale);
|
|
@@ -1217,20 +1243,20 @@ function baselineOfLabel(named, language) {
|
|
|
1217
1243
|
}
|
|
1218
1244
|
return named;
|
|
1219
1245
|
}
|
|
1220
|
-
function comparisonBaseline(vs, grain, label2,
|
|
1246
|
+
function comparisonBaseline(vs, grain, label2, toDate3, locale) {
|
|
1221
1247
|
const language = uiLanguage(resolveLocale(locale));
|
|
1222
1248
|
const nouns = BASELINE_NOUNS[language];
|
|
1223
1249
|
const named = label2?.trim();
|
|
1224
1250
|
if (named) return baselineOfLabel(named, language);
|
|
1225
|
-
if (vs === "custom") return nouns.custom;
|
|
1226
1251
|
if (vs === "target") return nouns.target;
|
|
1227
|
-
if (vs === "prior-year" &&
|
|
1252
|
+
if (vs === "prior-year" && toDate3) return nouns["prior-year"].unknown;
|
|
1253
|
+
if (vs !== "prior-year" && vs !== "prior-period") return nouns.custom;
|
|
1228
1254
|
return nouns[vs][grain ?? "unknown"];
|
|
1229
1255
|
}
|
|
1230
|
-
function comparisonLabel(vs, grain, label2,
|
|
1256
|
+
function comparisonLabel(vs, grain, label2, toDate3, locale) {
|
|
1231
1257
|
if (label2?.trim()) return label2;
|
|
1232
1258
|
const resolved = resolveLocale(locale);
|
|
1233
|
-
return LABEL_OF[uiLanguage(resolved)](comparisonBaseline(vs, grain, void 0,
|
|
1259
|
+
return LABEL_OF[uiLanguage(resolved)](comparisonBaseline(vs, grain, void 0, toDate3, resolved));
|
|
1234
1260
|
}
|
|
1235
1261
|
var toNumber = (v) => v === null || v === void 0 || Number.isNaN(v) ? null : v;
|
|
1236
1262
|
function computeComparison(current, comparison, options) {
|
|
@@ -1622,6 +1648,9 @@ function legendLines(items, plotWidth, reserve = 0) {
|
|
|
1622
1648
|
return Math.min(3, Math.max(1, Math.ceil(width / budget)));
|
|
1623
1649
|
}
|
|
1624
1650
|
var ACTIONS_RESERVE = 80;
|
|
1651
|
+
var BAR_MAX_WIDTH = 40;
|
|
1652
|
+
var BAR_RADIUS = 3;
|
|
1653
|
+
var STACK_GAP = 1.5;
|
|
1625
1654
|
var TIME_LABEL_WIDTH = 80;
|
|
1626
1655
|
var HIGHLIGHT_LABEL_WIDTH = 96;
|
|
1627
1656
|
var TARGET_NEAR_SHARE = 0.12;
|
|
@@ -1740,9 +1769,9 @@ function buildCartesianOption(cfg) {
|
|
|
1740
1769
|
const text = chartText(locale);
|
|
1741
1770
|
const provisionalLabel = cfg.provisionalLabel ?? text.provisional;
|
|
1742
1771
|
const stacked = kind !== "line" && type !== "default";
|
|
1743
|
-
const
|
|
1772
|
+
const percent2 = kind !== "line" && type === "percent";
|
|
1744
1773
|
const horizontalBars = kind === "bar" && layout === "vertical";
|
|
1745
|
-
const comparison = kind !== "bar" && !
|
|
1774
|
+
const comparison = kind !== "bar" && !percent2 ? cfg.comparison : void 0;
|
|
1746
1775
|
const compIdx = comparison ? categories.indexOf(comparison.category) : -1;
|
|
1747
1776
|
const mainIdx = categories.map((_, i) => i).filter((i) => i !== compIdx);
|
|
1748
1777
|
const seriesType = kind === "line" ? "default" : type;
|
|
@@ -1752,16 +1781,17 @@ function buildCartesianOption(cfg) {
|
|
|
1752
1781
|
const compared = cartesianSeriesValues(data, [categories[compIdx]], "default")[0];
|
|
1753
1782
|
return categories.map((_, i) => i === compIdx ? compared : main[mainIdx.indexOf(i)]);
|
|
1754
1783
|
})();
|
|
1784
|
+
const diverging = kind === "bar" && values.some((s) => s.some((v) => typeof v === "number" && v < 0));
|
|
1755
1785
|
const comparisonColor = guideColor ? withAlpha(guideColor, COMPARISON_OPACITY) : void 0;
|
|
1756
1786
|
const labels = data.map((row) => String(row[index] ?? ""));
|
|
1757
1787
|
const rawIndex = data.map((row) => row[index]);
|
|
1758
1788
|
const lastActual = labels.length - 1;
|
|
1759
1789
|
const provStart = categories.length > 0 ? provisionalStart(rawIndex, cfg.provisionalFrom) : -1;
|
|
1760
|
-
const
|
|
1790
|
+
const forecast2 = kind !== "bar" && !percent2 && cfg.forecast && cfg.forecast.data.length > 0 && data.length > 0 && categories.length > 0 ? cfg.forecast : void 0;
|
|
1761
1791
|
const forecastAt = [];
|
|
1762
|
-
if (
|
|
1792
|
+
if (forecast2) {
|
|
1763
1793
|
const position = new Map(labels.map((label2, i) => [label2, i]));
|
|
1764
|
-
for (const row of
|
|
1794
|
+
for (const row of forecast2.data) {
|
|
1765
1795
|
const label2 = String(row[index] ?? "");
|
|
1766
1796
|
let at = position.get(label2);
|
|
1767
1797
|
if (at === void 0 || at <= lastActual) {
|
|
@@ -1798,14 +1828,14 @@ function buildCartesianOption(cfg) {
|
|
|
1798
1828
|
(title, i) => nameLabels ? hasLabel(nameLabels, labels[i] ?? "") ? labelOf(labels[i] ?? "", nameLabels) : title : title
|
|
1799
1829
|
);
|
|
1800
1830
|
const highlight = cfg.highlight ?? defaultChartHighlight(kind, data, index);
|
|
1801
|
-
const stackTotals = stacked && !
|
|
1831
|
+
const stackTotals = stacked && !percent2 ? stackTotalsOf(mainIdx.map((i) => values[i])) : null;
|
|
1802
1832
|
const highlightSeries = (stackTotals ? mainIdx[mainIdx.length - 1] : mainIdx[0]) ?? 0;
|
|
1803
|
-
const shareHighlight =
|
|
1804
|
-
const picked = highlight !== "auto" || mainIdx.length === 0 ||
|
|
1833
|
+
const shareHighlight = percent2 && kind === "area";
|
|
1834
|
+
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
1835
|
const pointGap = plotWidth && labels.length > 1 ? plotWidth / (kind === "bar" ? labels.length : labels.length - 1) : null;
|
|
1806
1836
|
const latestOnly = cfg.highlight === void 0 && !shareHighlight && picked.length === 2;
|
|
1807
1837
|
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" && !
|
|
1838
|
+
const boundaryIndex = kind !== "bar" && !percent2 && cfg.forecast && cfg.forecast.data.length > 0 ? lastActual : -1;
|
|
1809
1839
|
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
1840
|
const perBarTarget = kind === "bar" && Boolean(targetKey);
|
|
1811
1841
|
const hasTarget = !perBarTarget && target !== void 0 && Number.isFinite(target);
|
|
@@ -1826,7 +1856,7 @@ function buildCartesianOption(cfg) {
|
|
|
1826
1856
|
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
1857
|
const below = (p) => !shareHighlight && !hideTargetLabel && nearTargetLabel(p, "under");
|
|
1828
1858
|
const targetLabelBelow = !shareHighlight && !hideTargetLabel && highlightPoints.some((p) => nearTargetLabel(p, "over"));
|
|
1829
|
-
const barTargets = perBarTarget && targetKey && !
|
|
1859
|
+
const barTargets = perBarTarget && targetKey && !percent2 && (stacked || categories.length === 1) ? barTargetValues(data, targetKey) : null;
|
|
1830
1860
|
const barTargetName = targetLabel ?? text.target;
|
|
1831
1861
|
const barTargetColor = labelColor ?? guideColor;
|
|
1832
1862
|
const markPointFor = (c, color) => c === highlightSeries && highlightPoints.length > 0 ? {
|
|
@@ -1867,14 +1897,16 @@ function buildCartesianOption(cfg) {
|
|
|
1867
1897
|
const seriesData = pad(values[c]);
|
|
1868
1898
|
if (kind === "bar") {
|
|
1869
1899
|
const rounded = horizontalBars ? [0, 4, 4, 0] : [4, 4, 0, 0];
|
|
1870
|
-
const radius = stacked
|
|
1900
|
+
const radius = stacked ? BAR_RADIUS : rounded;
|
|
1901
|
+
const gapStyle = stacked && labelBackground ? { borderColor: labelBackground, borderWidth: STACK_GAP } : {};
|
|
1871
1902
|
return {
|
|
1872
1903
|
id: SERIES_ID_PREFIX + category,
|
|
1873
1904
|
type: "bar",
|
|
1874
1905
|
name: category,
|
|
1875
1906
|
stack: stacked ? "total" : void 0,
|
|
1876
1907
|
barCategoryGap,
|
|
1877
|
-
|
|
1908
|
+
barMaxWidth: BAR_MAX_WIDTH,
|
|
1909
|
+
itemStyle: { color, borderRadius: radius, ...gapStyle },
|
|
1878
1910
|
markPoint: markPointFor(c, solid),
|
|
1879
1911
|
markLine: void 0,
|
|
1880
1912
|
data: seriesData.map((value, i) => {
|
|
@@ -1951,7 +1983,7 @@ function buildCartesianOption(cfg) {
|
|
|
1951
1983
|
symbolSize: 6,
|
|
1952
1984
|
showSymbol: showDots || data.length === 1,
|
|
1953
1985
|
// 100% stacked: the top band's upper edge is the 100% line, not a value — drawn, it reads as「this band = 100%」.
|
|
1954
|
-
lineStyle:
|
|
1986
|
+
lineStyle: percent2 && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, color, opacity: dimSeries ? DIMMED : 1 },
|
|
1955
1987
|
itemStyle: { color, opacity: dimSeries ? DIMMED : 1 },
|
|
1956
1988
|
// Hovering a series fades the others (not for a stack, where the bands read together).
|
|
1957
1989
|
emphasis: stacked ? void 0 : { focus: "series" },
|
|
@@ -1980,7 +2012,7 @@ function buildCartesianOption(cfg) {
|
|
|
1980
2012
|
symbol: "circle",
|
|
1981
2013
|
symbolSize: 6,
|
|
1982
2014
|
showSymbol: showDots || data.length === 1,
|
|
1983
|
-
lineStyle:
|
|
2015
|
+
lineStyle: percent2 && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, type: "dashed", color, opacity },
|
|
1984
2016
|
itemStyle: { color, opacity },
|
|
1985
2017
|
areaStyle: solid.areaStyle ? { ...solid.areaStyle, opacity: (solid.areaStyle.opacity ?? 1) * PROVISIONAL_OPACITY } : void 0,
|
|
1986
2018
|
markPoint: void 0,
|
|
@@ -2006,11 +2038,11 @@ function buildCartesianOption(cfg) {
|
|
|
2006
2038
|
data: []
|
|
2007
2039
|
});
|
|
2008
2040
|
}
|
|
2009
|
-
const forecastLabel =
|
|
2041
|
+
const forecastLabel = forecast2?.label ?? text.forecast;
|
|
2010
2042
|
const forecastBandAlpha = isDarkSurface(labelBackground) ? FORECAST_BAND_ALPHA_DARK : FORECAST_BAND_ALPHA_LIGHT;
|
|
2011
2043
|
const forecastValues = [];
|
|
2012
|
-
if (
|
|
2013
|
-
const category =
|
|
2044
|
+
if (forecast2) {
|
|
2045
|
+
const category = forecast2.category !== void 0 && categories.includes(forecast2.category) && forecast2.category !== comparison?.category ? forecast2.category : categories[mainIdx[0] ?? 0];
|
|
2014
2046
|
const c = categories.indexOf(category);
|
|
2015
2047
|
const color = palette[c] || void 0;
|
|
2016
2048
|
const actual = values[c];
|
|
@@ -2030,7 +2062,7 @@ function buildCartesianOption(cfg) {
|
|
|
2030
2062
|
range[start] = 0;
|
|
2031
2063
|
}
|
|
2032
2064
|
let hasBand = false;
|
|
2033
|
-
|
|
2065
|
+
forecast2.data.forEach((row, k) => {
|
|
2034
2066
|
const at = forecastAt[k];
|
|
2035
2067
|
line[at] = cellNumber(row.value);
|
|
2036
2068
|
const lo = cellNumber(row.lower);
|
|
@@ -2096,7 +2128,7 @@ function buildCartesianOption(cfg) {
|
|
|
2096
2128
|
const totals = labels.map(
|
|
2097
2129
|
(_, i) => stacked ? values.reduce((sum, v) => sum + Math.max(0, v[i] ?? 0), 0) : Math.max(0, ...values.map((v) => v[i] ?? 0))
|
|
2098
2130
|
);
|
|
2099
|
-
const top =
|
|
2131
|
+
const top = percent2 ? 100 : maxValue ?? Math.max(target, ...totals);
|
|
2100
2132
|
if (!(top > 0)) return "center";
|
|
2101
2133
|
const ratio = Math.min(1, Math.max(0, target / top));
|
|
2102
2134
|
const half = (estimateTextWidth(targetText) + 8) / 2;
|
|
@@ -2129,7 +2161,15 @@ function buildCartesianOption(cfg) {
|
|
|
2129
2161
|
}
|
|
2130
2162
|
});
|
|
2131
2163
|
}
|
|
2132
|
-
if (
|
|
2164
|
+
if (diverging) {
|
|
2165
|
+
markLines.push({
|
|
2166
|
+
name: "zero",
|
|
2167
|
+
[horizontalBars ? "xAxis" : "yAxis"]: 0,
|
|
2168
|
+
lineStyle: { type: "solid", width: 1, color: guideColor },
|
|
2169
|
+
label: { show: false }
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
if (forecast2) {
|
|
2133
2173
|
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
2174
|
markLines.push({
|
|
2135
2175
|
name: "forecast-boundary",
|
|
@@ -2260,6 +2300,8 @@ function buildCartesianOption(cfg) {
|
|
|
2260
2300
|
show: horizontalBars ? showYAxis : showXAxis,
|
|
2261
2301
|
boundaryGap: kind === "bar",
|
|
2262
2302
|
inverse: horizontalBars,
|
|
2303
|
+
// Diverging: the axis stays at the plot's edge (not at 0, where labels would hit the negative bars); the zero line is a mark line.
|
|
2304
|
+
axisLine: diverging ? { show: false, onZero: false } : void 0,
|
|
2263
2305
|
axisTick: { show: false },
|
|
2264
2306
|
axisLabel: {
|
|
2265
2307
|
hideOverlap: true,
|
|
@@ -2280,16 +2322,16 @@ function buildCartesianOption(cfg) {
|
|
|
2280
2322
|
const fit = kind === "line" && (fitToData ?? autoMinValue);
|
|
2281
2323
|
const valueSplitNumber = (() => {
|
|
2282
2324
|
if (!horizontalBars || !plotWidth) return void 0;
|
|
2283
|
-
const top =
|
|
2325
|
+
const top = percent2 ? 100 : maxValue ?? Math.max(0, ...(stacked ? stackTotalsOf(values) : values.flat()).map((v) => v ?? 0));
|
|
2284
2326
|
const categoryColumn = showYAxis ? (yAxisWidth ?? Math.min(160, Math.max(0, ...labels.map(estimateTextWidth)))) + 8 : 0;
|
|
2285
2327
|
const plot = plotWidth - categoryColumn - (highlightPoints.length > 0 ? 64 : 16) - 4;
|
|
2286
|
-
const text2 =
|
|
2328
|
+
const text2 = percent2 ? (v) => `${v}%` : (v) => axisFormatter(v);
|
|
2287
2329
|
return valueAxisSplitNumber(plot, [text2(0), text2(top)]);
|
|
2288
2330
|
})();
|
|
2289
2331
|
const limits = valueAxisLimits({ minValue, maxValue }, kind !== "line");
|
|
2290
2332
|
const splitNumber = horizontalBars ? valueSplitNumber : valueAxisSplitCount(plotHeight);
|
|
2291
2333
|
const tickFormatter = (() => {
|
|
2292
|
-
if (!cfg.axisTicks ||
|
|
2334
|
+
if (!cfg.axisTicks || percent2) return axisFormatter;
|
|
2293
2335
|
const extentValues = [];
|
|
2294
2336
|
const addAll = (list) => {
|
|
2295
2337
|
for (const v of list) if (v !== null && Number.isFinite(v)) extentValues.push(v);
|
|
@@ -2297,7 +2339,7 @@ function buildCartesianOption(cfg) {
|
|
|
2297
2339
|
if (stackTotals) addAll(stackTotals);
|
|
2298
2340
|
else values.forEach(addAll);
|
|
2299
2341
|
addAll(forecastValues);
|
|
2300
|
-
if (
|
|
2342
|
+
if (forecast2) addAll(forecast2.data.flatMap((row) => [cellNumber(row.lower), cellNumber(row.upper)]));
|
|
2301
2343
|
if (barTargets) addAll(barTargets);
|
|
2302
2344
|
if (hasTarget && target !== void 0) extentValues.push(target);
|
|
2303
2345
|
if (maxValue !== void 0 && Number.isFinite(maxValue)) extentValues.push(maxValue);
|
|
@@ -2314,8 +2356,8 @@ function buildCartesianOption(cfg) {
|
|
|
2314
2356
|
show: horizontalBars ? showXAxis : showYAxis,
|
|
2315
2357
|
// Bars and areas always start at 0 (a positive `minValue` can't lift them, a negative one makes room below);
|
|
2316
2358
|
// lines do too unless asked to fit the data. A limit never cuts the data: the axis widens to it.
|
|
2317
|
-
min:
|
|
2318
|
-
max:
|
|
2359
|
+
min: percent2 ? 0 : kind === "line" ? minValue === void 0 ? fit ? void 0 : zeroBasedMin : limits.min : limits.min,
|
|
2360
|
+
max: percent2 ? 100 : limits.max,
|
|
2319
2361
|
scale: fit && minValue == null,
|
|
2320
2362
|
splitNumber,
|
|
2321
2363
|
minInterval: allowDecimals ? void 0 : 1,
|
|
@@ -2323,7 +2365,7 @@ function buildCartesianOption(cfg) {
|
|
|
2323
2365
|
axisTick: { show: false },
|
|
2324
2366
|
splitLine: { show: showGridLines },
|
|
2325
2367
|
axisLabel: {
|
|
2326
|
-
formatter:
|
|
2368
|
+
formatter: percent2 ? (v) => `${v}%` : (v) => tickFormatter(v),
|
|
2327
2369
|
// Fitted to the data: the lowest tick would print over the first x label at the bottom left. The gridline stays.
|
|
2328
2370
|
showMinLabel: fit && minValue == null && !horizontalBars ? false : void 0,
|
|
2329
2371
|
// Horizontal: ticks that would still touch on a narrow plot are dropped.
|
|
@@ -2337,7 +2379,7 @@ function buildCartesianOption(cfg) {
|
|
|
2337
2379
|
};
|
|
2338
2380
|
const legendItems = [
|
|
2339
2381
|
...categories,
|
|
2340
|
-
...
|
|
2382
|
+
...forecast2 ? [forecastLabel] : [],
|
|
2341
2383
|
...provStart !== -1 ? [provisionalLabel] : [],
|
|
2342
2384
|
...barTargets ? [barTargetName] : [],
|
|
2343
2385
|
...hasTarget ? [targetText] : []
|
|
@@ -2348,7 +2390,7 @@ function buildCartesianOption(cfg) {
|
|
|
2348
2390
|
show: showLegend,
|
|
2349
2391
|
top: 0,
|
|
2350
2392
|
// Narrow: left-aligned and wrapping above the plot, so nothing is cut off at the right edge.
|
|
2351
|
-
...plotWidth !== void 0 && plotWidth < NARROW_PLOT_WIDTH ? { left: 0, right: 0 } : { right: reserveActions ? ACTIONS_RESERVE : 0 },
|
|
2393
|
+
...plotWidth !== void 0 && plotWidth < NARROW_PLOT_WIDTH ? { left: 0, right: 0 } : { left: 0, right: reserveActions ? ACTIONS_RESERVE : 0 },
|
|
2352
2394
|
// Plain wraps onto a second line at narrow widths instead of hiding items behind arrows.
|
|
2353
2395
|
type: "plain",
|
|
2354
2396
|
icon: "circle",
|
|
@@ -2358,7 +2400,7 @@ function buildCartesianOption(cfg) {
|
|
|
2358
2400
|
// The target marker is a tick, not a dot.
|
|
2359
2401
|
// 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
2402
|
data: legendItems.map(
|
|
2361
|
-
(name) => barTargets && name === barTargetName ? { name, icon: TARGET_LEGEND_ICON } :
|
|
2403
|
+
(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
2404
|
),
|
|
2363
2405
|
formatter: nameLabels ? (name) => labelOf(name, nameLabels) : void 0
|
|
2364
2406
|
},
|
|
@@ -2366,7 +2408,7 @@ function buildCartesianOption(cfg) {
|
|
|
2366
2408
|
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
2409
|
(horizontalBars && hasTarget ? 18 : 0) + // The label of a band over bars sits above the plot's top edge.
|
|
2368
2410
|
(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
|
-
(
|
|
2411
|
+
(forecast2 ? FORECAST_LABEL_ROOM : 0),
|
|
2370
2412
|
// Lines start and end on the outermost ticks, so the last x label (and an edge
|
|
2371
2413
|
// highlight) hangs past the plot; a horizontal bar's highlight sits past its end.
|
|
2372
2414
|
right: horizontalBars ? highlightPoints.length > 0 ? 64 : 16 : kind === "bar" ? 16 : LINE_RIGHT_MARGIN,
|
|
@@ -2397,7 +2439,7 @@ function buildCartesianOption(cfg) {
|
|
|
2397
2439
|
)
|
|
2398
2440
|
};
|
|
2399
2441
|
};
|
|
2400
|
-
const shares =
|
|
2442
|
+
const shares = percent2 ? cartesianSeriesValues(i <= lastActual ? [row] : [], categories, "percent") : null;
|
|
2401
2443
|
const legendRank = (p) => {
|
|
2402
2444
|
const at = categories.indexOf(p.seriesName ?? "");
|
|
2403
2445
|
return at === -1 ? categories.length : at;
|
|
@@ -2642,12 +2684,12 @@ function AreaChartImpl({
|
|
|
2642
2684
|
provisionalFrom,
|
|
2643
2685
|
provisionalLabel,
|
|
2644
2686
|
isLoading,
|
|
2645
|
-
loading,
|
|
2687
|
+
loading: loading2,
|
|
2646
2688
|
emptyState,
|
|
2647
2689
|
height,
|
|
2648
2690
|
chartOptions,
|
|
2649
2691
|
areaOptions,
|
|
2650
|
-
forecast,
|
|
2692
|
+
forecast: forecast2,
|
|
2651
2693
|
comparison,
|
|
2652
2694
|
highlight,
|
|
2653
2695
|
target,
|
|
@@ -2729,7 +2771,7 @@ function AreaChartImpl({
|
|
|
2729
2771
|
target,
|
|
2730
2772
|
targetLabel,
|
|
2731
2773
|
annotations,
|
|
2732
|
-
forecast,
|
|
2774
|
+
forecast: forecast2,
|
|
2733
2775
|
comparison: comparisonConfig,
|
|
2734
2776
|
comparisonFormat: format,
|
|
2735
2777
|
provisionalFrom,
|
|
@@ -2775,7 +2817,7 @@ function AreaChartImpl({
|
|
|
2775
2817
|
target,
|
|
2776
2818
|
targetLabel,
|
|
2777
2819
|
annotations,
|
|
2778
|
-
|
|
2820
|
+
forecast2,
|
|
2779
2821
|
comparisonConfig,
|
|
2780
2822
|
format,
|
|
2781
2823
|
height,
|
|
@@ -2803,7 +2845,7 @@ function AreaChartImpl({
|
|
|
2803
2845
|
ref: frameRef,
|
|
2804
2846
|
title,
|
|
2805
2847
|
summary: resolvedSummary,
|
|
2806
|
-
isLoading: isLoading ??
|
|
2848
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
2807
2849
|
isEmpty: data.length === 0,
|
|
2808
2850
|
emptyState,
|
|
2809
2851
|
height,
|
|
@@ -2837,7 +2879,7 @@ function BarChartImpl({
|
|
|
2837
2879
|
provisionalFrom,
|
|
2838
2880
|
provisionalLabel,
|
|
2839
2881
|
isLoading,
|
|
2840
|
-
loading,
|
|
2882
|
+
loading: loading2,
|
|
2841
2883
|
emptyState,
|
|
2842
2884
|
height,
|
|
2843
2885
|
chartOptions,
|
|
@@ -2993,7 +3035,7 @@ function BarChartImpl({
|
|
|
2993
3035
|
ref: frameRef,
|
|
2994
3036
|
title,
|
|
2995
3037
|
summary: resolvedSummary,
|
|
2996
|
-
isLoading: isLoading ??
|
|
3038
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
2997
3039
|
isEmpty: data.length === 0,
|
|
2998
3040
|
emptyState,
|
|
2999
3041
|
height,
|
|
@@ -3027,12 +3069,12 @@ function LineChartImpl({
|
|
|
3027
3069
|
provisionalFrom,
|
|
3028
3070
|
provisionalLabel,
|
|
3029
3071
|
isLoading,
|
|
3030
|
-
loading,
|
|
3072
|
+
loading: loading2,
|
|
3031
3073
|
emptyState,
|
|
3032
3074
|
height,
|
|
3033
3075
|
chartOptions,
|
|
3034
3076
|
lineOptions,
|
|
3035
|
-
forecast,
|
|
3077
|
+
forecast: forecast2,
|
|
3036
3078
|
comparison,
|
|
3037
3079
|
highlight,
|
|
3038
3080
|
target,
|
|
@@ -3115,7 +3157,7 @@ function LineChartImpl({
|
|
|
3115
3157
|
targetLabel,
|
|
3116
3158
|
targetLabelPosition,
|
|
3117
3159
|
annotations,
|
|
3118
|
-
forecast,
|
|
3160
|
+
forecast: forecast2,
|
|
3119
3161
|
comparison: comparisonConfig,
|
|
3120
3162
|
comparisonFormat: format,
|
|
3121
3163
|
provisionalFrom,
|
|
@@ -3162,7 +3204,7 @@ function LineChartImpl({
|
|
|
3162
3204
|
targetLabel,
|
|
3163
3205
|
targetLabelPosition,
|
|
3164
3206
|
annotations,
|
|
3165
|
-
|
|
3207
|
+
forecast2,
|
|
3166
3208
|
comparisonConfig,
|
|
3167
3209
|
format,
|
|
3168
3210
|
height,
|
|
@@ -3190,7 +3232,7 @@ function LineChartImpl({
|
|
|
3190
3232
|
ref: frameRef,
|
|
3191
3233
|
title,
|
|
3192
3234
|
summary: resolvedSummary,
|
|
3193
|
-
isLoading: isLoading ??
|
|
3235
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
3194
3236
|
isEmpty: data.length === 0,
|
|
3195
3237
|
emptyState,
|
|
3196
3238
|
height,
|
|
@@ -3234,7 +3276,7 @@ function DonutChartImpl({
|
|
|
3234
3276
|
actions,
|
|
3235
3277
|
fileName,
|
|
3236
3278
|
isLoading,
|
|
3237
|
-
loading,
|
|
3279
|
+
loading: loading2,
|
|
3238
3280
|
emptyState,
|
|
3239
3281
|
height,
|
|
3240
3282
|
chartOptions,
|
|
@@ -3328,7 +3370,7 @@ function DonutChartImpl({
|
|
|
3328
3370
|
ref: frameRef,
|
|
3329
3371
|
title,
|
|
3330
3372
|
summary: resolvedSummary,
|
|
3331
|
-
isLoading: isLoading ??
|
|
3373
|
+
isLoading: isLoading ?? loading2 ?? false,
|
|
3332
3374
|
isEmpty: data.length === 0,
|
|
3333
3375
|
emptyState,
|
|
3334
3376
|
height,
|
|
@@ -3531,7 +3573,8 @@ var CategoryBar = forwardRef(function CategoryBar2({ values = NO_ROWS6, colors,
|
|
|
3531
3573
|
style: { background: markerColor }
|
|
3532
3574
|
}
|
|
3533
3575
|
);
|
|
3534
|
-
|
|
3576
|
+
const labelled = props["aria-label"] != null || props["aria-labelledby"] != null;
|
|
3577
|
+
return /* @__PURE__ */ jsxs("div", { ref, "data-slot": "category-bar", role: labelled ? "group" : void 0, className: cn("w-full", className), ...props, children: [
|
|
3535
3578
|
showLabels && labels.length > 0 && /* @__PURE__ */ jsx("div", { "data-slot": "category-bar-labels", className: "relative mb-2 h-4 text-xs text-muted-foreground tabular-nums", children: labels.map((label2, i) => /* @__PURE__ */ jsx(
|
|
3536
3579
|
"span",
|
|
3537
3580
|
{
|
|
@@ -3566,7 +3609,7 @@ var CategoryBar = forwardRef(function CategoryBar2({ values = NO_ROWS6, colors,
|
|
|
3566
3609
|
),
|
|
3567
3610
|
style: { left: `${markerPosition}%` },
|
|
3568
3611
|
children: marker.tooltip ? /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
3569
|
-
/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx("
|
|
3612
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx("button", { type: "button", "aria-label": marker.tooltip, className: "block cursor-default py-1" }), children: markerDot }),
|
|
3570
3613
|
/* @__PURE__ */ jsx(TooltipContent, { children: marker.tooltip })
|
|
3571
3614
|
] }) : markerDot
|
|
3572
3615
|
}
|
|
@@ -3875,8 +3918,19 @@ var STEP_LABEL_MIN_SLOT = 44;
|
|
|
3875
3918
|
function stepLabelWidth(plotWidth, steps) {
|
|
3876
3919
|
return Math.max(24, stepSlotWidth(plotWidth, steps) - 8);
|
|
3877
3920
|
}
|
|
3878
|
-
function
|
|
3879
|
-
|
|
3921
|
+
function longestWordWidth(names) {
|
|
3922
|
+
let widest = 0;
|
|
3923
|
+
for (const name of names) {
|
|
3924
|
+
for (const word of name.split(/\s+/)) {
|
|
3925
|
+
let w = 0;
|
|
3926
|
+
for (const ch of word) w += ch.charCodeAt(0) > 11903 ? 12 : 7;
|
|
3927
|
+
widest = Math.max(widest, w);
|
|
3928
|
+
}
|
|
3929
|
+
}
|
|
3930
|
+
return widest;
|
|
3931
|
+
}
|
|
3932
|
+
function stepLabelLayout(plotWidth, steps, names = []) {
|
|
3933
|
+
return stepSlotWidth(plotWidth, steps) >= STEP_LABEL_MIN_SLOT && longestWordWidth(names) <= stepLabelWidth(plotWidth, steps) ? { width: stepLabelWidth(plotWidth, steps), rotate: 0 } : { rotate: 45 };
|
|
3880
3934
|
}
|
|
3881
3935
|
function waterfallValueLabelPlan(steps, texts, plotWidth) {
|
|
3882
3936
|
const show = steps.map((s) => s.kind !== "gap");
|
|
@@ -4017,7 +4071,7 @@ function buildWaterfallOption(config) {
|
|
|
4017
4071
|
interval: 0,
|
|
4018
4072
|
hideOverlap: false,
|
|
4019
4073
|
formatter: (value) => labelOf(value, labels),
|
|
4020
|
-
...config.plotWidth ? { ...stepLabelLayout(config.plotWidth, steps.length), overflow: "break" } : {}
|
|
4074
|
+
...config.plotWidth ? { ...stepLabelLayout(config.plotWidth, steps.length, steps.map((st) => labelOf(st.name, labels))), overflow: "break" } : {}
|
|
4021
4075
|
}
|
|
4022
4076
|
},
|
|
4023
4077
|
yAxis: {
|
|
@@ -4378,8 +4432,8 @@ function buildScatterOption(config) {
|
|
|
4378
4432
|
legend: {
|
|
4379
4433
|
show: showLegend,
|
|
4380
4434
|
...legendBelow ? { bottom: 0 } : { top: 0 },
|
|
4381
|
-
// With a heading the legend reads from the left, after it; otherwise it sits at the
|
|
4382
|
-
...headingWidth > 0 ? { left: headingWidth } :
|
|
4435
|
+
// With a heading the legend reads from the left, after it; otherwise it sits at the left edge (as in every chart).
|
|
4436
|
+
...headingWidth > 0 ? { left: headingWidth } : { left: 0, ...legendBelow ? {} : { right: config.reserveActions ? 72 : 0 } },
|
|
4383
4437
|
type: "plain",
|
|
4384
4438
|
icon: "circle",
|
|
4385
4439
|
itemWidth: 8,
|
|
@@ -4392,7 +4446,9 @@ function buildScatterOption(config) {
|
|
|
4392
4446
|
top: (showLegend && !legendBelow ? 12 + 24 * legendLineCount : 12) + (guides.some((line) => line.axis === "x" && line.label) && (!showLegend || legendBelow) ? 14 : 0),
|
|
4393
4447
|
right: 24,
|
|
4394
4448
|
bottom: legendBelow ? 24 + 24 * legendLineCount : 24,
|
|
4395
|
-
left:
|
|
4449
|
+
// The rotated y axis name lies outside `containLabel`'s reach (its right edge is `nameGap` left of the axis line): the
|
|
4450
|
+
// margin holds its height, so the name is never cut at the chart's left edge.
|
|
4451
|
+
left: 36,
|
|
4396
4452
|
containLabel: true
|
|
4397
4453
|
},
|
|
4398
4454
|
...headingWidth > 0 ? {
|
|
@@ -4441,7 +4497,7 @@ function buildScatterOption(config) {
|
|
|
4441
4497
|
label: {
|
|
4442
4498
|
show: keptLabels === null,
|
|
4443
4499
|
position: "right",
|
|
4444
|
-
distance:
|
|
4500
|
+
distance: 8,
|
|
4445
4501
|
fontSize: 11,
|
|
4446
4502
|
color: config.textColors?.label,
|
|
4447
4503
|
// Plain text: an outline / shadow reads as a halo on a dark surface.
|
|
@@ -4449,7 +4505,8 @@ function buildScatterOption(config) {
|
|
|
4449
4505
|
textShadowBlur: 0,
|
|
4450
4506
|
formatter: (p) => p.data?.point?.label ?? ""
|
|
4451
4507
|
},
|
|
4452
|
-
|
|
4508
|
+
// A name stays beside its own dot: one that would collide with another is hidden, not pushed onto a neighbouring dot.
|
|
4509
|
+
labelLayout: { hideOverlap: true }
|
|
4453
4510
|
} : {},
|
|
4454
4511
|
// The guide lines hang off the first series (a markLine belongs to one).
|
|
4455
4512
|
...i === 0 && guides.length > 0 ? {
|
|
@@ -4623,7 +4680,7 @@ var TEXT11 = {
|
|
|
4623
4680
|
ja: { legend: "\u51E1\u4F8B" },
|
|
4624
4681
|
en: { legend: "Legend" }
|
|
4625
4682
|
};
|
|
4626
|
-
var Legend = forwardRef(function Legend2({ categories, colors, labels, layout = "horizontal", ariaLabel, className }, ref) {
|
|
4683
|
+
var Legend = forwardRef(function Legend2({ categories, colors, labels, layout = "horizontal", hidden = [], onHiddenChange, ariaLabel, className }, ref) {
|
|
4627
4684
|
const t = TEXT11[uiLanguage(useLocale())];
|
|
4628
4685
|
if (categories.length === 0) return null;
|
|
4629
4686
|
const swatches = legendColors(categories, colors);
|
|
@@ -4635,10 +4692,23 @@ var Legend = forwardRef(function Legend2({ categories, colors, labels, layout =
|
|
|
4635
4692
|
"data-layout": layout,
|
|
4636
4693
|
"aria-label": ariaLabel ?? t.legend,
|
|
4637
4694
|
className: cn("m-0 flex list-none gap-x-4 gap-y-1 p-0 text-xs", layout === "vertical" ? "flex-col" : "flex-row flex-wrap", className),
|
|
4638
|
-
children: categories.map((category, i) =>
|
|
4639
|
-
|
|
4640
|
-
/* @__PURE__ */
|
|
4641
|
-
|
|
4695
|
+
children: categories.map((category, i) => {
|
|
4696
|
+
const off = hidden.includes(category);
|
|
4697
|
+
const chip = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4698
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("size-2.5 shrink-0 rounded-[2px]", off && "opacity-30"), style: { background: swatches[i] } }),
|
|
4699
|
+
/* @__PURE__ */ jsx("span", { className: cn("min-w-0 truncate text-muted-foreground", off && "line-through opacity-60"), children: labelOf(category, labels) })
|
|
4700
|
+
] });
|
|
4701
|
+
return /* @__PURE__ */ jsx("li", { "data-slot": "legend-item", "data-hidden": off || void 0, className: "flex min-w-0 items-center gap-1.5", children: onHiddenChange ? /* @__PURE__ */ jsx(
|
|
4702
|
+
"button",
|
|
4703
|
+
{
|
|
4704
|
+
type: "button",
|
|
4705
|
+
"aria-pressed": !off,
|
|
4706
|
+
onClick: () => onHiddenChange(off ? hidden.filter((c) => c !== category) : [...hidden, category]),
|
|
4707
|
+
className: "flex min-w-0 cursor-pointer items-center gap-1.5 rounded-sm outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
4708
|
+
children: chip
|
|
4709
|
+
}
|
|
4710
|
+
) : chip }, category);
|
|
4711
|
+
})
|
|
4642
4712
|
}
|
|
4643
4713
|
);
|
|
4644
4714
|
});
|
|
@@ -5083,10 +5153,10 @@ var HEATMAP_CELL_PADDING = 4;
|
|
|
5083
5153
|
var HEATMAP_BUCKET_MID = 20;
|
|
5084
5154
|
function heatmapCellFontSize(plotWidth, columns, rowNames, texts) {
|
|
5085
5155
|
const widest = (items) => items.reduce((m, t) => Math.max(m, estimateTextWidth2(t)), 0);
|
|
5086
|
-
const
|
|
5156
|
+
const cell2 = (plotWidth + HEATMAP_BUCKET_MID - HEATMAP_GRID_X - (widest(rowNames) + 8)) / Math.max(1, columns);
|
|
5087
5157
|
const text = widest(texts);
|
|
5088
|
-
if (text + HEATMAP_CELL_PADDING <=
|
|
5089
|
-
return text * 10 / 12 + HEATMAP_CELL_PADDING <=
|
|
5158
|
+
if (text + HEATMAP_CELL_PADDING <= cell2) return 12;
|
|
5159
|
+
return text * 10 / 12 + HEATMAP_CELL_PADDING <= cell2 ? 10 : null;
|
|
5090
5160
|
}
|
|
5091
5161
|
var TEXT13 = {
|
|
5092
5162
|
ja: {
|
|
@@ -5114,24 +5184,24 @@ function buildHeatmapOption(config) {
|
|
|
5114
5184
|
const base = config.color || "#6366f1";
|
|
5115
5185
|
const range = [mixIntoSurface(base, config.surfaceColor, HEATMAP_FLOOR_SHARE), base];
|
|
5116
5186
|
const gapColor = "rgba(0, 0, 0, 0)";
|
|
5117
|
-
const flagged = (
|
|
5118
|
-
const tooltip = (
|
|
5119
|
-
const head = `${escapeHtml2(label2(model.ys[
|
|
5120
|
-
const main =
|
|
5187
|
+
const flagged = (cell2) => cell2.flagged ? { borderColor: config.flagColor, borderWidth: 3, borderType: "solid" } : { borderColor: "transparent", borderWidth: 1 };
|
|
5188
|
+
const tooltip = (cell2) => {
|
|
5189
|
+
const head = `${escapeHtml2(label2(model.ys[cell2.yi]))} ${escapeHtml2(label2(model.xs[cell2.xi]))}`;
|
|
5190
|
+
const main = cell2.value === null ? EMPTY_VALUE : escapeHtml2(valueFormatter(cell2.value));
|
|
5121
5191
|
const extras = (config.details ?? []).map((key) => {
|
|
5122
|
-
const n =
|
|
5192
|
+
const n = cell2.row ? toChartNumber(cell2.row[key]) : null;
|
|
5123
5193
|
return `${escapeHtml2(label2(key))} ${n === null ? EMPTY_VALUE : escapeHtml2(valueFormatter(n))}`;
|
|
5124
5194
|
}).join(t.join);
|
|
5125
|
-
const flag =
|
|
5195
|
+
const flag = cell2.flagged ? `${t.join}${t.flagged}` : "";
|
|
5126
5196
|
return `${head} ${main}${extras ? `${t.join}${extras}` : ""}${flag}`;
|
|
5127
5197
|
};
|
|
5128
5198
|
const cellFont = config.plotWidth ? heatmapCellFontSize(
|
|
5129
5199
|
config.plotWidth,
|
|
5130
5200
|
model.xs.length,
|
|
5131
5201
|
model.ys.map(label2),
|
|
5132
|
-
model.cells.flatMap((
|
|
5202
|
+
model.cells.flatMap((cell2) => cell2.value === null ? [] : [valueFormatter(cell2.value)])
|
|
5133
5203
|
) : 12;
|
|
5134
|
-
const point = (
|
|
5204
|
+
const point = (cell2) => ({ value: [cell2.xi, cell2.yi, cell2.value], cell: cell2 });
|
|
5135
5205
|
const option = {
|
|
5136
5206
|
animation: false,
|
|
5137
5207
|
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.cell) } : { show: false },
|
|
@@ -5179,11 +5249,11 @@ function buildHeatmapOption(config) {
|
|
|
5179
5249
|
{
|
|
5180
5250
|
id: "values",
|
|
5181
5251
|
type: "heatmap",
|
|
5182
|
-
data: present.map((
|
|
5183
|
-
...point(
|
|
5184
|
-
itemStyle: flagged(
|
|
5252
|
+
data: present.map((cell2) => ({
|
|
5253
|
+
...point(cell2),
|
|
5254
|
+
itemStyle: flagged(cell2),
|
|
5185
5255
|
label: {
|
|
5186
|
-
color: heatmapRatio(
|
|
5256
|
+
color: heatmapRatio(cell2.value, model.min, model.max) >= HEATMAP_LIGHT_TEXT_FROM ? config.darkCellText : config.lightCellText
|
|
5187
5257
|
}
|
|
5188
5258
|
})),
|
|
5189
5259
|
label: {
|
|
@@ -5198,7 +5268,7 @@ function buildHeatmapOption(config) {
|
|
|
5198
5268
|
{
|
|
5199
5269
|
id: "gaps",
|
|
5200
5270
|
type: "heatmap",
|
|
5201
|
-
data: missing.map((
|
|
5271
|
+
data: missing.map((cell2) => ({ value: [cell2.xi, cell2.yi, 0], cell: cell2, itemStyle: flagged(cell2) })),
|
|
5202
5272
|
label: { show: false },
|
|
5203
5273
|
itemStyle: {
|
|
5204
5274
|
color: gapColor,
|
|
@@ -5234,7 +5304,7 @@ function heatmapDataTable(model, names, formatter, labels, locale) {
|
|
|
5234
5304
|
}
|
|
5235
5305
|
function buildHeatmapSummary(model, formatter, title, labels, locale) {
|
|
5236
5306
|
let best;
|
|
5237
|
-
for (const
|
|
5307
|
+
for (const cell2 of model.cells) if (cell2.value !== null && (!best || cell2.value > best.value)) best = cell2;
|
|
5238
5308
|
if (!best) return void 0;
|
|
5239
5309
|
const label2 = (key) => labels?.[key] ?? key;
|
|
5240
5310
|
const head = title ? `${title}: ` : "";
|
|
@@ -5348,118 +5418,3180 @@ function HeatmapLegend({
|
|
|
5348
5418
|
var Heatmap = forwardRef(HeatmapImpl);
|
|
5349
5419
|
Heatmap.displayName = "Heatmap";
|
|
5350
5420
|
var TEXT14 = {
|
|
5421
|
+
ja: { item: "\u9805\u76EE", chart: (keys) => `${keys.join("\u30FB")}\u306E\u63A8\u79FB(\u4E0A\u6BB5 \u68D2\u3001\u4E0B\u6BB5 \u7DDA)` },
|
|
5422
|
+
en: { item: "Item", chart: (keys) => `Trend of ${keys.join(", ")} (bars on top, line below)` }
|
|
5423
|
+
};
|
|
5424
|
+
var NO_ROWS12 = [];
|
|
5425
|
+
var NO_KEYS = [];
|
|
5426
|
+
var PANEL_GAP = 20;
|
|
5427
|
+
var LEGEND_LINE = 24;
|
|
5428
|
+
var DEFAULT_HEIGHT = 400;
|
|
5429
|
+
var DEFAULT_Y_AXIS_WIDTH = 64;
|
|
5430
|
+
function pairedPalette(barKeys, lineKeys, barColors, lineColors) {
|
|
5431
|
+
const bars = seriesColorsFor(barKeys, barColors, CHART_OTHER_COLOR);
|
|
5432
|
+
const line = lineColors ? seriesColorsFor(lineKeys, lineColors, CHART_OTHER_COLOR) : lineKeys.map((_, i) => DEFAULT_CHART_COLORS[(barKeys.length + i) % DEFAULT_CHART_COLORS.length]);
|
|
5433
|
+
return { bars, line };
|
|
5434
|
+
}
|
|
5435
|
+
function buildPairedOption(cfg) {
|
|
5436
|
+
const { data, index, labels, barKeys, lineKeys, height, heightRatio, yAxisWidth, showLegend, showTooltip } = cfg;
|
|
5437
|
+
const legendNames = [...barKeys, ...lineKeys];
|
|
5438
|
+
const legendWidth = legendNames.reduce((sum, name) => sum + [...labels?.[name] ?? name].length * 12 + 29, 0);
|
|
5439
|
+
const budget = Math.max(80, Math.min(cfg.plotWidth ?? 720, 720) - (cfg.reserveActions ? 80 : 0));
|
|
5440
|
+
const legendRows = showLegend ? Math.min(3, Math.max(1, Math.ceil(legendWidth / budget))) : 0;
|
|
5441
|
+
const top = (showLegend ? 12 + LEGEND_LINE * legendRows : 12) + 4;
|
|
5442
|
+
const room = Math.max(120, height - top - PANEL_GAP);
|
|
5443
|
+
const [a, b] = heightRatio;
|
|
5444
|
+
const upper = Math.round(room * a / (a + b));
|
|
5445
|
+
const common = {
|
|
5446
|
+
data,
|
|
5447
|
+
index,
|
|
5448
|
+
labels,
|
|
5449
|
+
showLegend: false,
|
|
5450
|
+
showTooltip: false,
|
|
5451
|
+
highlight: "none",
|
|
5452
|
+
yAxisWidth,
|
|
5453
|
+
reducedMotion: false,
|
|
5454
|
+
locale: cfg.locale
|
|
5455
|
+
};
|
|
5456
|
+
const barOpt = buildCartesianOption({
|
|
5457
|
+
...common,
|
|
5458
|
+
kind: "bar",
|
|
5459
|
+
categories: barKeys,
|
|
5460
|
+
palette: cfg.barPalette,
|
|
5461
|
+
valueFormatter: cfg.barFormatter,
|
|
5462
|
+
axisFormatter: cfg.barAxis,
|
|
5463
|
+
axisTicks: cfg.barTicks,
|
|
5464
|
+
type: cfg.type,
|
|
5465
|
+
showXAxis: false,
|
|
5466
|
+
yAxisLabel: cfg.barAxisLabel,
|
|
5467
|
+
maxValue: cfg.barMax,
|
|
5468
|
+
plotWidth: cfg.plotWidth,
|
|
5469
|
+
plotHeight: upper
|
|
5470
|
+
});
|
|
5471
|
+
const lineOpt = buildCartesianOption({
|
|
5472
|
+
...common,
|
|
5473
|
+
kind: "line",
|
|
5474
|
+
categories: lineKeys,
|
|
5475
|
+
palette: cfg.linePalette,
|
|
5476
|
+
valueFormatter: cfg.lineFormatter,
|
|
5477
|
+
axisFormatter: cfg.lineAxis,
|
|
5478
|
+
axisTicks: cfg.lineTicks,
|
|
5479
|
+
fitToData: cfg.fitToData,
|
|
5480
|
+
showXAxis: true,
|
|
5481
|
+
yAxisLabel: cfg.lineAxisLabel,
|
|
5482
|
+
minValue: cfg.lineMin,
|
|
5483
|
+
maxValue: cfg.lineMax,
|
|
5484
|
+
guideColor: cfg.guideColor,
|
|
5485
|
+
plotWidth: cfg.plotWidth,
|
|
5486
|
+
plotHeight: room - upper
|
|
5487
|
+
});
|
|
5488
|
+
const times = timeTooltipLabels(data.map((row) => row[index]), cfg.locale);
|
|
5489
|
+
const titles = labelRows(data, index, labels).map((row, i) => String(times?.[i] ?? row[index] ?? ""));
|
|
5490
|
+
const cell2 = (row, key) => {
|
|
5491
|
+
const v = row[key];
|
|
5492
|
+
const n = typeof v === "number" ? v : typeof v === "string" && v.trim() !== "" ? Number(v) : NaN;
|
|
5493
|
+
return Number.isFinite(n) ? n : null;
|
|
5494
|
+
};
|
|
5495
|
+
const rowsOf = (row, keys, palette) => keys.map((key, i) => ({ name: labelOf(key, labels), color: palette[i] || "currentColor", value: cell2(row, key) }));
|
|
5496
|
+
const emptyTitle = tooltipHtml("", [], String, [], cfg.locale);
|
|
5497
|
+
const tooltipFormatter = (params) => {
|
|
5498
|
+
const list = Array.isArray(params) ? params : [params];
|
|
5499
|
+
const i = list[0]?.dataIndex ?? 0;
|
|
5500
|
+
const row = data[i] ?? {};
|
|
5501
|
+
const upperHtml = tooltipHtml(titles[i] ?? "", rowsOf(row, barKeys, cfg.barPalette), cfg.barFormatter, [], cfg.locale);
|
|
5502
|
+
const lowerHtml = tooltipHtml("", rowsOf(row, lineKeys, cfg.linePalette), cfg.lineFormatter, [], cfg.locale).slice(emptyTitle.length);
|
|
5503
|
+
return `${upperHtml}${lowerHtml}`;
|
|
5504
|
+
};
|
|
5505
|
+
const lineX = { ...lineOpt.xAxis, id: "x-line", gridIndex: 1, boundaryGap: true };
|
|
5506
|
+
const barX = { ...barOpt.xAxis, id: "x-bars", gridIndex: 0, show: true, axisLine: { show: false }, axisLabel: { show: false } };
|
|
5507
|
+
const gridLeft = barOpt.grid.left ?? 4;
|
|
5508
|
+
const right = 16;
|
|
5509
|
+
const option = {
|
|
5510
|
+
...barOpt,
|
|
5511
|
+
legend: { ...barOpt.legend, show: showLegend, data: legendNames, left: 0, right: cfg.reserveActions ? 80 : 0 },
|
|
5512
|
+
grid: [
|
|
5513
|
+
{ top, height: upper, left: gridLeft, right, containLabel: true },
|
|
5514
|
+
{ top: top + upper + PANEL_GAP, bottom: 4, left: gridLeft, right, containLabel: true }
|
|
5515
|
+
],
|
|
5516
|
+
xAxis: [barX, lineX],
|
|
5517
|
+
yAxis: [
|
|
5518
|
+
{ ...barOpt.yAxis, id: "y-bars", gridIndex: 0 },
|
|
5519
|
+
{ ...lineOpt.yAxis, id: "y-line", gridIndex: 1 }
|
|
5520
|
+
],
|
|
5521
|
+
series: [
|
|
5522
|
+
...barOpt.series.map((s) => ({ ...s, xAxisIndex: 0, yAxisIndex: 0 })),
|
|
5523
|
+
...lineOpt.series.map((s) => ({ ...s, xAxisIndex: 1, yAxisIndex: 1 }))
|
|
5524
|
+
],
|
|
5525
|
+
tooltip: {
|
|
5526
|
+
...barOpt.tooltip,
|
|
5527
|
+
show: showTooltip,
|
|
5528
|
+
trigger: "axis",
|
|
5529
|
+
axisPointer: { type: "line", link: [{ xAxisIndex: "all" }] },
|
|
5530
|
+
formatter: tooltipFormatter
|
|
5531
|
+
},
|
|
5532
|
+
axisPointer: { link: [{ xAxisIndex: "all" }] }
|
|
5533
|
+
};
|
|
5534
|
+
return mergeChartOptions(option, cfg.chartOptions);
|
|
5535
|
+
}
|
|
5536
|
+
function PairedChartImpl({
|
|
5537
|
+
data = NO_ROWS12,
|
|
5538
|
+
index,
|
|
5539
|
+
labels,
|
|
5540
|
+
bars,
|
|
5541
|
+
line,
|
|
5542
|
+
title,
|
|
5543
|
+
summary,
|
|
5544
|
+
actions,
|
|
5545
|
+
fileName,
|
|
5546
|
+
isLoading,
|
|
5547
|
+
emptyState,
|
|
5548
|
+
height = DEFAULT_HEIGHT,
|
|
5549
|
+
heightRatio = [3, 2],
|
|
5550
|
+
yAxisWidth = DEFAULT_Y_AXIS_WIDTH,
|
|
5551
|
+
showLegend = true,
|
|
5552
|
+
showTooltip = true,
|
|
5553
|
+
chartOptions,
|
|
5554
|
+
ariaLabel,
|
|
5555
|
+
className,
|
|
5556
|
+
...rest
|
|
5557
|
+
}, ref) {
|
|
5558
|
+
const { ref: frameRef, width: plotWidth, tokens } = useChartSurface(ref);
|
|
5559
|
+
const barKeys = bars?.categories ?? NO_KEYS;
|
|
5560
|
+
const lineKeys = line?.categories ?? NO_KEYS;
|
|
5561
|
+
const locale = useLocale();
|
|
5562
|
+
const t = TEXT14[uiLanguage(locale)];
|
|
5563
|
+
const barFormatter = useMemo(() => resolveValueFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5564
|
+
const lineFormatter = useMemo(() => resolveValueFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5565
|
+
const barAxis = useMemo(() => resolveAxisFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5566
|
+
const lineAxis = useMemo(() => resolveAxisFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5567
|
+
const barTicks = useMemo(() => resolveAxisTickFormatter(bars?.valueFormatter, bars?.format, locale), [bars?.valueFormatter, bars?.format, locale]);
|
|
5568
|
+
const lineTicks = useMemo(() => resolveAxisTickFormatter(line?.valueFormatter, line?.format, locale), [line?.valueFormatter, line?.format, locale]);
|
|
5569
|
+
const palette = useMemo(() => {
|
|
5570
|
+
const p = pairedPalette(barKeys, lineKeys, bars?.colors, line?.colors);
|
|
5571
|
+
return { bars: resolveChartColors(p.bars, tokens), line: resolveChartColors(p.line, tokens) };
|
|
5572
|
+
}, [barKeys, lineKeys, bars?.colors, line?.colors, tokens]);
|
|
5573
|
+
const option = useMemo(
|
|
5574
|
+
() => buildPairedOption({
|
|
5575
|
+
data,
|
|
5576
|
+
index,
|
|
5577
|
+
labels,
|
|
5578
|
+
barKeys,
|
|
5579
|
+
lineKeys,
|
|
5580
|
+
barPalette: palette.bars,
|
|
5581
|
+
linePalette: palette.line,
|
|
5582
|
+
barFormatter,
|
|
5583
|
+
lineFormatter,
|
|
5584
|
+
barAxis,
|
|
5585
|
+
lineAxis,
|
|
5586
|
+
barTicks,
|
|
5587
|
+
lineTicks,
|
|
5588
|
+
type: bars?.type,
|
|
5589
|
+
fitToData: line?.fitToData,
|
|
5590
|
+
barAxisLabel: bars?.yAxisLabel,
|
|
5591
|
+
lineAxisLabel: line?.yAxisLabel,
|
|
5592
|
+
barMax: bars?.maxValue,
|
|
5593
|
+
lineMax: line?.maxValue,
|
|
5594
|
+
lineMin: line?.minValue,
|
|
5595
|
+
height,
|
|
5596
|
+
heightRatio,
|
|
5597
|
+
yAxisWidth,
|
|
5598
|
+
showLegend,
|
|
5599
|
+
showTooltip,
|
|
5600
|
+
reserveActions: Boolean(actions),
|
|
5601
|
+
plotWidth,
|
|
5602
|
+
guideColor: tokens?.mutedForeground ?? void 0,
|
|
5603
|
+
chartOptions,
|
|
5604
|
+
locale
|
|
5605
|
+
}),
|
|
5606
|
+
[
|
|
5607
|
+
data,
|
|
5608
|
+
index,
|
|
5609
|
+
labels,
|
|
5610
|
+
barKeys,
|
|
5611
|
+
lineKeys,
|
|
5612
|
+
palette,
|
|
5613
|
+
barFormatter,
|
|
5614
|
+
lineFormatter,
|
|
5615
|
+
barAxis,
|
|
5616
|
+
lineAxis,
|
|
5617
|
+
barTicks,
|
|
5618
|
+
lineTicks,
|
|
5619
|
+
bars?.type,
|
|
5620
|
+
bars?.yAxisLabel,
|
|
5621
|
+
bars?.maxValue,
|
|
5622
|
+
line?.fitToData,
|
|
5623
|
+
line?.yAxisLabel,
|
|
5624
|
+
line?.maxValue,
|
|
5625
|
+
line?.minValue,
|
|
5626
|
+
height,
|
|
5627
|
+
heightRatio,
|
|
5628
|
+
yAxisWidth,
|
|
5629
|
+
showLegend,
|
|
5630
|
+
showTooltip,
|
|
5631
|
+
actions,
|
|
5632
|
+
plotWidth,
|
|
5633
|
+
tokens,
|
|
5634
|
+
chartOptions,
|
|
5635
|
+
locale
|
|
5636
|
+
]
|
|
5637
|
+
);
|
|
5638
|
+
const dataTable = useMemo(() => {
|
|
5639
|
+
const upper = chartDataTable(labelRows(data, index, labels), index, barKeys, barFormatter, labels, t.item);
|
|
5640
|
+
const lower = chartDataTable(labelRows(data, index, labels), index, lineKeys, lineFormatter, labels, t.item);
|
|
5641
|
+
const times = timeTooltipLabels(data.map((row) => row[index]), locale);
|
|
5642
|
+
return {
|
|
5643
|
+
...upper,
|
|
5644
|
+
columns: [...upper.columns, ...lower.columns.slice(1)],
|
|
5645
|
+
rows: upper.rows.map((r, i) => [times?.[i] ?? r[0] ?? "", ...r.slice(1), ...(lower.rows[i] ?? []).slice(1)])
|
|
5646
|
+
};
|
|
5647
|
+
}, [data, index, labels, barKeys, lineKeys, barFormatter, lineFormatter, t, locale]);
|
|
5648
|
+
const label2 = ariaLabel ?? t.chart([...barKeys, ...lineKeys]);
|
|
5649
|
+
return /* @__PURE__ */ jsx(
|
|
5650
|
+
ChartFrame,
|
|
5651
|
+
{
|
|
5652
|
+
...chartRootProps(rest),
|
|
5653
|
+
ref: frameRef,
|
|
5654
|
+
title,
|
|
5655
|
+
summary: summary === "auto" ? void 0 : summary,
|
|
5656
|
+
isLoading: isLoading ?? false,
|
|
5657
|
+
isEmpty: data.length === 0,
|
|
5658
|
+
emptyState,
|
|
5659
|
+
height,
|
|
5660
|
+
variant: "bar",
|
|
5661
|
+
ariaLabel: label2,
|
|
5662
|
+
className,
|
|
5663
|
+
dataTable,
|
|
5664
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: summary === "auto" ? void 0 : summary } })
|
|
5665
|
+
}
|
|
5666
|
+
);
|
|
5667
|
+
}
|
|
5668
|
+
var PairedChart = forwardRef(PairedChartImpl);
|
|
5669
|
+
PairedChart.displayName = "PairedChart";
|
|
5670
|
+
|
|
5671
|
+
// ../squadkit/registry/lib/distribution.ts
|
|
5672
|
+
function toFinite(value) {
|
|
5673
|
+
const n = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
5674
|
+
return Number.isFinite(n) ? n : null;
|
|
5675
|
+
}
|
|
5676
|
+
function sortedNumbers(values) {
|
|
5677
|
+
const out = [];
|
|
5678
|
+
for (const v of values) {
|
|
5679
|
+
const n = toFinite(v);
|
|
5680
|
+
if (n !== null) out.push(n);
|
|
5681
|
+
}
|
|
5682
|
+
return out.sort((a, b) => a - b);
|
|
5683
|
+
}
|
|
5684
|
+
function quantile(sorted, p) {
|
|
5685
|
+
if (sorted.length === 0) return null;
|
|
5686
|
+
const pos = (sorted.length - 1) * Math.min(1, Math.max(0, p));
|
|
5687
|
+
const lo = Math.floor(pos);
|
|
5688
|
+
const hi = Math.ceil(pos);
|
|
5689
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
|
5690
|
+
}
|
|
5691
|
+
function boxStats(values, whisker = 1.5) {
|
|
5692
|
+
const sorted = sortedNumbers(values);
|
|
5693
|
+
if (sorted.length === 0) return null;
|
|
5694
|
+
const q1 = quantile(sorted, 0.25);
|
|
5695
|
+
const median = quantile(sorted, 0.5);
|
|
5696
|
+
const q3 = quantile(sorted, 0.75);
|
|
5697
|
+
const reach = Math.max(0, whisker) * (q3 - q1);
|
|
5698
|
+
const low = q1 - reach;
|
|
5699
|
+
const high = q3 + reach;
|
|
5700
|
+
const inside = sorted.filter((v) => v >= low && v <= high);
|
|
5701
|
+
return {
|
|
5702
|
+
count: sorted.length,
|
|
5703
|
+
min: sorted[0],
|
|
5704
|
+
max: sorted[sorted.length - 1],
|
|
5705
|
+
q1,
|
|
5706
|
+
median,
|
|
5707
|
+
q3,
|
|
5708
|
+
whiskerLow: inside[0] ?? q1,
|
|
5709
|
+
whiskerHigh: inside[inside.length - 1] ?? q3,
|
|
5710
|
+
outliers: sorted.filter((v) => v < low || v > high)
|
|
5711
|
+
};
|
|
5712
|
+
}
|
|
5713
|
+
function niceBinWidth(span, target = 10) {
|
|
5714
|
+
if (!(span > 0) || !Number.isFinite(span)) return 1;
|
|
5715
|
+
const raw = span / target;
|
|
5716
|
+
const pow = 10 ** Math.floor(Math.log10(raw));
|
|
5717
|
+
const f = raw / pow;
|
|
5718
|
+
return (f <= 1 ? 1 : f <= 2 ? 2 : f <= 2.5 ? 2.5 : f <= 5 ? 5 : 10) * pow;
|
|
5719
|
+
}
|
|
5720
|
+
var MAX_BINS = 200;
|
|
5721
|
+
function tidy(n, step) {
|
|
5722
|
+
const digits = Math.min(100, Math.max(0, Math.ceil(-Math.log10(step)) + 6));
|
|
5723
|
+
const rounded = Number(n.toFixed(digits));
|
|
5724
|
+
return Number.isFinite(rounded) ? rounded : n;
|
|
5725
|
+
}
|
|
5726
|
+
function histogramBins(values, config = {}) {
|
|
5727
|
+
const sorted = sortedNumbers(values);
|
|
5728
|
+
if (sorted.length === 0) return [];
|
|
5729
|
+
const width = config.binWidth !== void 0 && Number.isFinite(config.binWidth) && config.binWidth > 0 ? config.binWidth : null;
|
|
5730
|
+
const lo = sorted[0];
|
|
5731
|
+
const hi = sorted[sorted.length - 1];
|
|
5732
|
+
const cap = config.max !== void 0 && Number.isFinite(config.max) ? config.max : null;
|
|
5733
|
+
const end = cap !== null ? cap : hi;
|
|
5734
|
+
const first = config.start !== void 0 && Number.isFinite(config.start) ? config.start : null;
|
|
5735
|
+
let step = width ?? niceBinWidth((cap !== null ? Math.min(hi, cap) : hi) - (first ?? lo));
|
|
5736
|
+
const start = first ?? tidy(Math.floor(lo / step) * step, step);
|
|
5737
|
+
const countFor = (w) => Math.ceil((end - start) / w - 1e-9);
|
|
5738
|
+
if (countFor(step) > MAX_BINS) step = niceBinWidth(end - start, MAX_BINS);
|
|
5739
|
+
const n = Math.min(MAX_BINS, Math.max(cap === null ? 1 : 0, countFor(step)));
|
|
5740
|
+
const bins = [];
|
|
5741
|
+
for (let i = 0; i < n; i++) {
|
|
5742
|
+
const from = i === 0 ? start : bins[i - 1].to;
|
|
5743
|
+
let to = tidy(start + (i + 1) * step, step);
|
|
5744
|
+
if (!(to > from)) to = start + (i + 1) * step;
|
|
5745
|
+
if (cap !== null) to = Math.min(to, cap);
|
|
5746
|
+
bins.push({ from, to, count: 0, overflow: false });
|
|
5747
|
+
}
|
|
5748
|
+
const inRange = cap === null ? sorted : sorted.filter((v) => v < cap);
|
|
5749
|
+
const over = cap === null ? [] : sorted.filter((v) => v >= cap);
|
|
5750
|
+
for (const v of inRange) {
|
|
5751
|
+
if (v < start || bins.length === 0) continue;
|
|
5752
|
+
const i = Math.min(bins.length - 1, Math.floor((v - start) / step + 1e-9));
|
|
5753
|
+
bins[i].count += 1;
|
|
5754
|
+
}
|
|
5755
|
+
if (over.length > 0) bins.push({ from: cap, to: Math.max(hi, cap), count: over.length, overflow: true });
|
|
5756
|
+
return bins;
|
|
5757
|
+
}
|
|
5758
|
+
var TEXT15 = {
|
|
5351
5759
|
ja: {
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
exportCsv: "CSV \u3092\u66F8\u304D\u51FA\u3059"
|
|
5760
|
+
countUnit: "\u4EF6",
|
|
5761
|
+
atOrAbove: (from) => `${from}\u4EE5\u4E0A`,
|
|
5762
|
+
range: (from, to) => `${from}\u301C${to}`,
|
|
5763
|
+
count: (n, unit) => `${n}${unit}`,
|
|
5764
|
+
countHeader: (unit) => `${unit}\u6570`,
|
|
5765
|
+
share: "\u69CB\u6210\u6BD4",
|
|
5766
|
+
value: "\u5024",
|
|
5767
|
+
valueInSentence: "\u5024",
|
|
5768
|
+
threshold: "\u3057\u304D\u3044\u5024",
|
|
5769
|
+
peak: (total, bin, count, unit) => `${total}${unit}\u306E\u3046\u3061\u6700\u3082\u591A\u3044\u306E\u306F ${bin}\uFF08${count}${unit}\uFF09`,
|
|
5770
|
+
overThreshold: (name, value, count, share) => `${name}\uFF08${value}\uFF09\u4EE5\u4E0A\u306F ${count}\uFF08${share}\uFF09`,
|
|
5771
|
+
sentenceJoin: "\u3002",
|
|
5772
|
+
chart: (label2) => `${label2}\u306E\u30D2\u30B9\u30C8\u30B0\u30E9\u30E0`
|
|
5366
5773
|
},
|
|
5367
5774
|
en: {
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
exportCsv: "Export CSV"
|
|
5775
|
+
countUnit: "",
|
|
5776
|
+
atOrAbove: (from) => `${from}+`,
|
|
5777
|
+
range: (from, to) => `${from}\u2013${to}`,
|
|
5778
|
+
count: (n, unit) => unit ? `${n} ${unit}` : `${n}`,
|
|
5779
|
+
countHeader: () => "Count",
|
|
5780
|
+
share: "Share",
|
|
5781
|
+
value: "Value",
|
|
5782
|
+
valueInSentence: "values",
|
|
5783
|
+
threshold: "Threshold",
|
|
5784
|
+
peak: (total, bin, count, unit) => `Most common: ${bin} (${unit ? `${count} ${unit}` : count} of ${total})`,
|
|
5785
|
+
overThreshold: (name, value, count, share) => `At or above ${name} (${value}): ${count} (${share})`,
|
|
5786
|
+
sentenceJoin: ". ",
|
|
5787
|
+
chart: (label2) => `Histogram of ${label2}`
|
|
5382
5788
|
}
|
|
5383
5789
|
};
|
|
5384
|
-
var
|
|
5385
|
-
function
|
|
5386
|
-
|
|
5790
|
+
var textOf4 = (locale) => TEXT15[uiLanguage(resolveLocale(locale))];
|
|
5791
|
+
function binLabel(bin, formatter, locale) {
|
|
5792
|
+
const t = textOf4(locale);
|
|
5793
|
+
return bin.overflow ? t.atOrAbove(formatter(bin.from)) : t.range(formatter(bin.from), formatter(bin.to));
|
|
5794
|
+
}
|
|
5795
|
+
function thresholdPosition(bins, value) {
|
|
5796
|
+
const lastIndex = bins.length - 1;
|
|
5797
|
+
const i = bins.findIndex((b, k) => k === lastIndex ? value >= b.from && (b.overflow || value <= b.to) : value >= b.from && value < b.to);
|
|
5798
|
+
const bin = bins[i];
|
|
5799
|
+
if (!bin) return null;
|
|
5800
|
+
return bin.overflow ? i - 0.5 : i - 0.5 + (value - bin.from) / (bin.to - bin.from);
|
|
5801
|
+
}
|
|
5802
|
+
function countAtOrAbove(values, value, from = -Infinity) {
|
|
5803
|
+
return sortedNumbers(values).filter((v) => v >= value && v >= from).length;
|
|
5804
|
+
}
|
|
5805
|
+
var percent = (n, total) => total === 0 ? "0%" : `${Math.round(n / total * 100)}%`;
|
|
5806
|
+
function buildHistogramSummary(bins, formatter, config) {
|
|
5807
|
+
if (bins.length === 0 || config.total === 0) return void 0;
|
|
5808
|
+
const t = textOf4(config.locale);
|
|
5809
|
+
const count = (n) => t.count(n, config.countUnit);
|
|
5810
|
+
const peak = bins.reduce((a, b) => b.count > a.count ? b : a);
|
|
5811
|
+
const parts = [t.peak(config.total, binLabel(peak, formatter, config.locale), peak.count, config.countUnit)];
|
|
5812
|
+
if (config.threshold && config.atOrAbove !== void 0) {
|
|
5813
|
+
const name = config.threshold.label ?? t.threshold;
|
|
5814
|
+
parts.push(t.overThreshold(name, formatter(config.threshold.value), count(config.atOrAbove), percent(config.atOrAbove, config.total)));
|
|
5815
|
+
}
|
|
5816
|
+
return parts.join(t.sentenceJoin);
|
|
5387
5817
|
}
|
|
5388
|
-
function
|
|
5389
|
-
|
|
5818
|
+
function histogramDataTable(bins, formatter, config) {
|
|
5819
|
+
const t = textOf4(config.locale);
|
|
5820
|
+
return {
|
|
5821
|
+
columns: [config.valueLabel ?? t.value, t.countHeader(config.countUnit), t.share],
|
|
5822
|
+
rows: bins.map((b) => [binLabel(b, formatter, config.locale), `${b.count}`, percent(b.count, config.total)])
|
|
5823
|
+
};
|
|
5390
5824
|
}
|
|
5391
|
-
function
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
return text === EMPTY_VALUE ? String(value) : text;
|
|
5396
|
-
}
|
|
5397
|
-
if (typeof value !== "number") return String(value);
|
|
5398
|
-
return formatValue(value, resolveFormat(meta.format, row), locale);
|
|
5825
|
+
function thresholdPoint(bins, value) {
|
|
5826
|
+
const pos = thresholdPosition(bins, value) ?? 0;
|
|
5827
|
+
const index = Math.min(bins.length - 1, Math.max(0, Math.round(pos)));
|
|
5828
|
+
return [index, 0, pos - index];
|
|
5399
5829
|
}
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
const tone = typeof value === "number" ? toneForValue(value, thresholds) : void 0;
|
|
5409
|
-
if (!tone) return /* @__PURE__ */ jsx("div", { className: "text-right tabular-nums", children: text });
|
|
5410
|
-
const { Icon, text: toneClass } = THRESHOLD_TONE_STYLES[tone];
|
|
5411
|
-
const label2 = t.tones[tone];
|
|
5412
|
-
return /* @__PURE__ */ jsxs(
|
|
5413
|
-
"div",
|
|
5414
|
-
{
|
|
5415
|
-
"data-slot": "data-table-threshold-cell",
|
|
5416
|
-
"data-tone": tone,
|
|
5417
|
-
className: cn("flex items-center justify-end gap-1.5 tabular-nums", toneClass),
|
|
5830
|
+
function thresholdRenderer(style) {
|
|
5831
|
+
return (params, api) => {
|
|
5832
|
+
const { x: gx, y: gy, width, height } = params.coordSys;
|
|
5833
|
+
const band = Math.abs(api.size([1, 0])[0] ?? 0);
|
|
5834
|
+
const x = (api.coord([api.value(0), 0])[0] ?? 0) + api.value(2) * band;
|
|
5835
|
+
const nearRight = x > gx + width * 0.7;
|
|
5836
|
+
return {
|
|
5837
|
+
type: "group",
|
|
5418
5838
|
children: [
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5839
|
+
{
|
|
5840
|
+
type: "line",
|
|
5841
|
+
shape: { x1: x, y1: gy, x2: x, y2: gy + height },
|
|
5842
|
+
style: { stroke: style.lineColor || "#888", lineWidth: 1.5, lineDash: [4, 4], fill: "none" }
|
|
5843
|
+
},
|
|
5844
|
+
{
|
|
5845
|
+
type: "text",
|
|
5846
|
+
style: {
|
|
5847
|
+
text: style.text,
|
|
5848
|
+
x,
|
|
5849
|
+
y: gy - 4,
|
|
5850
|
+
fill: style.labelColor || "#888",
|
|
5851
|
+
textAlign: nearRight ? "right" : "left",
|
|
5852
|
+
textVerticalAlign: "bottom"
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5422
5855
|
]
|
|
5423
|
-
}
|
|
5424
|
-
|
|
5856
|
+
};
|
|
5857
|
+
};
|
|
5425
5858
|
}
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
const
|
|
5429
|
-
const
|
|
5430
|
-
const
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5859
|
+
var ROW_STYLE3 = "display:flex;align-items:center;gap:8px;line-height:20px";
|
|
5860
|
+
function buildHistogramOption(config) {
|
|
5861
|
+
const { locale } = config;
|
|
5862
|
+
const t = textOf4(locale);
|
|
5863
|
+
const { bins, total, colors, valueFormatter, threshold, showTooltip = true, countUnit = t.countUnit } = config;
|
|
5864
|
+
const pos = threshold ? thresholdPosition(bins, threshold.value) : null;
|
|
5865
|
+
const option = {
|
|
5866
|
+
...chartMotion(false),
|
|
5867
|
+
legend: { show: false },
|
|
5868
|
+
grid: { top: pos === null ? 16 : 28, right: 16, bottom: 4, left: 4, containLabel: true },
|
|
5869
|
+
tooltip: {
|
|
5870
|
+
show: showTooltip,
|
|
5871
|
+
trigger: "axis",
|
|
5872
|
+
axisPointer: { type: "shadow" },
|
|
5873
|
+
formatter: (params) => {
|
|
5874
|
+
const list = Array.isArray(params) ? params : [params];
|
|
5875
|
+
const bin = bins[(list.find((p) => p.seriesId === "count") ?? list[0])?.dataIndex ?? 0];
|
|
5876
|
+
if (!bin) return "";
|
|
5877
|
+
const color = escapeHtml((bin.overflow ? colors.overflow : colors.bar) || "currentColor");
|
|
5878
|
+
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>`;
|
|
5879
|
+
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));
|
|
5435
5880
|
}
|
|
5436
5881
|
},
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
}
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5882
|
+
xAxis: {
|
|
5883
|
+
type: "category",
|
|
5884
|
+
data: bins.map((b) => binLabel(b, valueFormatter, locale)),
|
|
5885
|
+
axisTick: { show: false },
|
|
5886
|
+
axisLabel: { hideOverlap: true }
|
|
5887
|
+
},
|
|
5888
|
+
yAxis: {
|
|
5889
|
+
type: "value",
|
|
5890
|
+
minInterval: 1,
|
|
5891
|
+
axisLine: { show: false },
|
|
5892
|
+
axisTick: { show: false },
|
|
5893
|
+
splitLine: { show: true }
|
|
5894
|
+
},
|
|
5895
|
+
series: [
|
|
5896
|
+
{
|
|
5897
|
+
id: "count",
|
|
5898
|
+
type: "bar",
|
|
5899
|
+
barCategoryGap: "6%",
|
|
5900
|
+
data: bins.map((b) => ({
|
|
5901
|
+
value: b.count,
|
|
5902
|
+
itemStyle: { color: (b.overflow ? colors.overflow : colors.bar) || void 0, borderRadius: [2, 2, 0, 0] }
|
|
5903
|
+
})),
|
|
5904
|
+
label: { show: false, color: config.labelColor, fontVariantNumeric: "tabular-nums" }
|
|
5905
|
+
},
|
|
5906
|
+
...threshold && pos !== null ? [
|
|
5907
|
+
{
|
|
5908
|
+
id: "threshold",
|
|
5909
|
+
type: "custom",
|
|
5910
|
+
silent: true,
|
|
5911
|
+
z: 10,
|
|
5912
|
+
animation: false,
|
|
5913
|
+
tooltip: { show: false },
|
|
5914
|
+
encode: { x: 0, y: 1 },
|
|
5915
|
+
// [階級の添字, 0(y の位置は使わない), 階級の中での位置(左端 -0.5 〜 右端 0.5)]
|
|
5916
|
+
data: [thresholdPoint(bins, threshold.value)],
|
|
5917
|
+
renderItem: thresholdRenderer({
|
|
5918
|
+
text: `${threshold.label ?? t.threshold} ${valueFormatter(threshold.value)}`,
|
|
5919
|
+
lineColor: config.lineColor,
|
|
5920
|
+
labelColor: config.labelColor
|
|
5921
|
+
})
|
|
5922
|
+
}
|
|
5923
|
+
] : []
|
|
5924
|
+
]
|
|
5925
|
+
};
|
|
5926
|
+
return mergeChartOptions(option, config.chartOptions);
|
|
5446
5927
|
}
|
|
5447
|
-
var
|
|
5448
|
-
function
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5928
|
+
var NO_VALUES = [];
|
|
5929
|
+
var HistogramChart = forwardRef(function HistogramChart2({
|
|
5930
|
+
values = NO_VALUES,
|
|
5931
|
+
binWidth,
|
|
5932
|
+
start,
|
|
5933
|
+
max,
|
|
5934
|
+
threshold,
|
|
5935
|
+
countUnit: countUnitProp,
|
|
5936
|
+
valueLabel,
|
|
5937
|
+
format,
|
|
5938
|
+
valueFormatter: valueFormatterProp,
|
|
5939
|
+
showTooltip = true,
|
|
5940
|
+
title,
|
|
5941
|
+
summary,
|
|
5942
|
+
actions,
|
|
5943
|
+
fileName,
|
|
5944
|
+
isLoading,
|
|
5945
|
+
emptyState,
|
|
5946
|
+
height,
|
|
5947
|
+
chartOptions,
|
|
5948
|
+
ariaLabel,
|
|
5949
|
+
className,
|
|
5950
|
+
...rest
|
|
5951
|
+
}, ref) {
|
|
5952
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
5953
|
+
const locale = useLocale();
|
|
5954
|
+
const t = TEXT15[uiLanguage(locale)];
|
|
5955
|
+
const countUnit = countUnitProp ?? t.countUnit;
|
|
5956
|
+
const valueFormatter = useMemo(
|
|
5957
|
+
() => valueFormatterProp ?? (format === void 0 ? toValueFormatter("number", locale) : resolveValueFormatter(void 0, format, locale)),
|
|
5958
|
+
[valueFormatterProp, format, locale]
|
|
5959
|
+
);
|
|
5960
|
+
const bins = useMemo(() => histogramBins(values, { binWidth, start, max }), [values, binWidth, start, max]);
|
|
5961
|
+
const total = useMemo(() => bins.reduce((sum, b) => sum + b.count, 0), [bins]);
|
|
5962
|
+
const colors = useMemo(() => {
|
|
5963
|
+
const [bar = ""] = resolveChartColors(["chart-1"], tokens);
|
|
5964
|
+
return { bar, overflow: resolveFillColors(tokens).neutral };
|
|
5965
|
+
}, [tokens]);
|
|
5966
|
+
const option = useMemo(
|
|
5967
|
+
() => buildHistogramOption({
|
|
5968
|
+
bins,
|
|
5969
|
+
total,
|
|
5970
|
+
colors,
|
|
5971
|
+
valueFormatter,
|
|
5972
|
+
countUnit,
|
|
5973
|
+
threshold,
|
|
5974
|
+
showTooltip,
|
|
5975
|
+
labelColor: tokens?.foreground ?? void 0,
|
|
5976
|
+
lineColor: tokens?.mutedForeground ?? void 0,
|
|
5977
|
+
chartOptions,
|
|
5978
|
+
locale
|
|
5979
|
+
}),
|
|
5980
|
+
[bins, total, colors, valueFormatter, countUnit, threshold, showTooltip, tokens, chartOptions, locale]
|
|
5981
|
+
);
|
|
5982
|
+
const dataTable = useMemo(
|
|
5983
|
+
() => histogramDataTable(bins, valueFormatter, { total, countUnit, valueLabel, locale }),
|
|
5984
|
+
[bins, valueFormatter, total, countUnit, valueLabel, locale]
|
|
5985
|
+
);
|
|
5986
|
+
const resolvedSummary = useMemo(
|
|
5987
|
+
() => summary === "auto" ? buildHistogramSummary(bins, valueFormatter, {
|
|
5988
|
+
total,
|
|
5989
|
+
countUnit,
|
|
5990
|
+
threshold,
|
|
5991
|
+
atOrAbove: threshold ? countAtOrAbove(values, threshold.value, bins[0]?.from) : void 0,
|
|
5992
|
+
locale
|
|
5993
|
+
}) : summary,
|
|
5994
|
+
[summary, bins, valueFormatter, total, countUnit, threshold, values, locale]
|
|
5995
|
+
);
|
|
5996
|
+
const label2 = ariaLabel ?? t.chart(valueLabel ?? t.valueInSentence);
|
|
5997
|
+
return /* @__PURE__ */ jsx(
|
|
5998
|
+
ChartFrame,
|
|
5999
|
+
{
|
|
6000
|
+
...chartRootProps(rest),
|
|
6001
|
+
ref: frameRef,
|
|
6002
|
+
title,
|
|
6003
|
+
summary: resolvedSummary,
|
|
6004
|
+
isLoading: isLoading ?? false,
|
|
6005
|
+
isEmpty: bins.length === 0,
|
|
6006
|
+
emptyState,
|
|
6007
|
+
height,
|
|
6008
|
+
variant: "bar",
|
|
6009
|
+
ariaLabel: label2,
|
|
6010
|
+
className,
|
|
6011
|
+
dataTable,
|
|
6012
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } })
|
|
6013
|
+
}
|
|
6014
|
+
);
|
|
6015
|
+
});
|
|
6016
|
+
HistogramChart.displayName = "HistogramChart";
|
|
6017
|
+
var TEXT16 = {
|
|
6018
|
+
ja: {
|
|
6019
|
+
value: "\u5024",
|
|
6020
|
+
valueInSentence: "\u5024",
|
|
6021
|
+
medianOf: (name, label2, median) => `${name}\u306E${label2}\u306E\u4E2D\u592E\u5024\u306F ${median}`,
|
|
6022
|
+
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`,
|
|
6023
|
+
outliers: (n) => `\u5916\u308C\u5024\u306F ${n} \u4EF6`,
|
|
6024
|
+
sentenceJoin: "\u3002",
|
|
6025
|
+
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"],
|
|
6026
|
+
listJoin: "\u3001",
|
|
6027
|
+
none: "\u306A\u3057",
|
|
6028
|
+
tooltipTitle: (name, label2, count) => `${name}\uFF08${label2}\u30FB${count}\u4EF6\uFF09`,
|
|
6029
|
+
max: "\u6700\u5927\uFF08\u3072\u3052\uFF09",
|
|
6030
|
+
q3: "\u7B2C3\u56DB\u5206\u4F4D",
|
|
6031
|
+
median: "\u4E2D\u592E\u5024",
|
|
6032
|
+
q1: "\u7B2C1\u56DB\u5206\u4F4D",
|
|
6033
|
+
min: "\u6700\u5C0F\uFF08\u3072\u3052\uFF09",
|
|
6034
|
+
outlierRow: "\u5916\u308C\u5024",
|
|
6035
|
+
chart: (label2) => `${label2}\u306E\u7BB1\u3072\u3052\u56F3`
|
|
6036
|
+
},
|
|
6037
|
+
en: {
|
|
6038
|
+
value: "Value",
|
|
6039
|
+
valueInSentence: "value",
|
|
6040
|
+
medianOf: (name, label2, median) => `Median ${label2} for ${name}: ${median}`,
|
|
6041
|
+
medianRange: (label2, top, topMedian, low, lowMedian) => `Highest median ${label2}: ${top} (${topMedian}); lowest: ${low} (${lowMedian})`,
|
|
6042
|
+
outliers: (n) => n === 1 ? "1 outlier" : `${n} outliers`,
|
|
6043
|
+
sentenceJoin: ". ",
|
|
6044
|
+
columns: ["Group", "Count", "Min (whisker)", "Q1", "Median", "Q3", "Max (whisker)", "Outliers"],
|
|
6045
|
+
listJoin: ", ",
|
|
6046
|
+
none: "None",
|
|
6047
|
+
tooltipTitle: (name, label2, count) => `${name} (${label2}, n=${count})`,
|
|
6048
|
+
max: "Max (whisker)",
|
|
6049
|
+
q3: "Q3",
|
|
6050
|
+
median: "Median",
|
|
6051
|
+
q1: "Q1",
|
|
6052
|
+
min: "Min (whisker)",
|
|
6053
|
+
outlierRow: "Outliers",
|
|
6054
|
+
chart: (label2) => `Box plot of ${label2}`
|
|
6055
|
+
}
|
|
6056
|
+
};
|
|
6057
|
+
var textOf5 = (locale) => TEXT16[uiLanguage(resolveLocale(locale))];
|
|
6058
|
+
function boxPlotEntries(groups, whisker) {
|
|
6059
|
+
return groups.map((g) => ({ name: g.name, stats: boxStats(g.values, whisker) }));
|
|
6060
|
+
}
|
|
6061
|
+
function buildBoxPlotSummary(entries, formatter, valueLabel, locale) {
|
|
6062
|
+
const t = textOf5(locale);
|
|
6063
|
+
const label2 = valueLabel ?? t.valueInSentence;
|
|
6064
|
+
const real = entries.filter((e) => e.stats !== null);
|
|
6065
|
+
if (real.length === 0) return void 0;
|
|
6066
|
+
const top = real.reduce((a, b) => b.stats.median > a.stats.median ? b : a);
|
|
6067
|
+
const low = real.reduce((a, b) => b.stats.median < a.stats.median ? b : a);
|
|
6068
|
+
const parts = [
|
|
6069
|
+
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))
|
|
6070
|
+
];
|
|
6071
|
+
const outliers = real.reduce((sum, e) => sum + e.stats.outliers.length, 0);
|
|
6072
|
+
if (outliers > 0) parts.push(t.outliers(outliers));
|
|
6073
|
+
return parts.join(t.sentenceJoin);
|
|
6074
|
+
}
|
|
6075
|
+
function boxPlotDataTable(entries, formatter, locale) {
|
|
6076
|
+
const t = textOf5(locale);
|
|
6077
|
+
const f = (n) => n === void 0 ? EMPTY_VALUE : formatter(n);
|
|
6078
|
+
return {
|
|
6079
|
+
columns: [...t.columns],
|
|
6080
|
+
rows: entries.map(({ name, stats: s }) => [
|
|
6081
|
+
name,
|
|
6082
|
+
s ? `${s.count}` : EMPTY_VALUE,
|
|
6083
|
+
f(s?.whiskerLow),
|
|
6084
|
+
f(s?.q1),
|
|
6085
|
+
f(s?.median),
|
|
6086
|
+
f(s?.q3),
|
|
6087
|
+
f(s?.whiskerHigh),
|
|
6088
|
+
s ? s.outliers.length > 0 ? s.outliers.map(formatter).join(t.listJoin) : t.none : EMPTY_VALUE
|
|
6089
|
+
])
|
|
6090
|
+
};
|
|
6091
|
+
}
|
|
6092
|
+
var ROW_STYLE4 = "display:flex;align-items:center;gap:8px;line-height:20px";
|
|
6093
|
+
var BOX_SHARE = 0.4;
|
|
6094
|
+
function boxPlotRenderer(color, outliers) {
|
|
6095
|
+
return (_params, api) => {
|
|
6096
|
+
const x = api.coord([api.value(0), api.value(3)])[0] ?? 0;
|
|
6097
|
+
const y = (dim) => api.coord([api.value(0), api.value(dim)])[1] ?? 0;
|
|
6098
|
+
const band = Math.abs(api.size([1, 0])[0] ?? 0);
|
|
6099
|
+
const half = band * BOX_SHARE / 2;
|
|
6100
|
+
const cap = half / 2;
|
|
6101
|
+
const [yLow, yQ1, yMed, yQ3, yHigh] = [y(1), y(2), y(3), y(4), y(5)];
|
|
6102
|
+
const stroke = { stroke: color, lineWidth: 1.5, fill: "none" };
|
|
6103
|
+
const line = (x1, y1, x2, y2) => ({ type: "line", shape: { x1, y1, x2, y2 }, style: stroke });
|
|
6104
|
+
return {
|
|
6105
|
+
type: "group",
|
|
6106
|
+
children: [
|
|
6107
|
+
line(x, yHigh, x, yQ3),
|
|
6108
|
+
line(x, yQ1, x, yLow),
|
|
6109
|
+
line(x - cap, yHigh, x + cap, yHigh),
|
|
6110
|
+
line(x - cap, yLow, x + cap, yLow),
|
|
6111
|
+
{
|
|
6112
|
+
type: "rect",
|
|
6113
|
+
shape: { x: x - half, y: Math.min(yQ3, yQ1), width: half * 2, height: Math.abs(yQ1 - yQ3), r: 2 },
|
|
6114
|
+
style: { fill: withAlpha(color, 0.25), stroke: color, lineWidth: 1.5 }
|
|
6115
|
+
},
|
|
6116
|
+
line(x - half, yMed, x + half, yMed),
|
|
6117
|
+
...(outliers[api.value(0)] ?? []).map((v) => ({
|
|
6118
|
+
type: "circle",
|
|
6119
|
+
shape: { cx: x, cy: api.coord([api.value(0), v])[1] ?? 0, r: 3 },
|
|
6120
|
+
style: { fill: "transparent", stroke: color, lineWidth: 1.5 }
|
|
6121
|
+
}))
|
|
6122
|
+
]
|
|
6123
|
+
};
|
|
6124
|
+
};
|
|
6125
|
+
}
|
|
6126
|
+
function buildBoxPlotOption(config) {
|
|
6127
|
+
const t = textOf5(config.locale);
|
|
6128
|
+
const { entries, valueFormatter, showTooltip = true, valueLabel = t.value } = config;
|
|
6129
|
+
const axisFormatter = config.axisFormatter ?? valueFormatter;
|
|
6130
|
+
const color = config.color || "#888";
|
|
6131
|
+
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]] : []);
|
|
6132
|
+
const entryAt = (i) => entries[i];
|
|
6133
|
+
const option = {
|
|
6134
|
+
...chartMotion(false),
|
|
6135
|
+
legend: { show: false },
|
|
6136
|
+
grid: { top: 16, right: 16, bottom: 4, left: 4, containLabel: true },
|
|
6137
|
+
tooltip: {
|
|
6138
|
+
show: showTooltip,
|
|
6139
|
+
trigger: "item",
|
|
6140
|
+
formatter: (params) => {
|
|
6141
|
+
const entry = entryAt(params.value?.[0] ?? -1);
|
|
6142
|
+
const s = entry?.stats;
|
|
6143
|
+
if (!entry || !s) return "";
|
|
6144
|
+
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>`;
|
|
6145
|
+
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)) : "");
|
|
6146
|
+
}
|
|
6147
|
+
},
|
|
6148
|
+
xAxis: { type: "category", data: entries.map((e) => e.name), axisTick: { show: false }, axisLabel: { interval: 0, hideOverlap: false } },
|
|
6149
|
+
yAxis: {
|
|
6150
|
+
type: "value",
|
|
6151
|
+
axisLine: { show: false },
|
|
6152
|
+
axisTick: { show: false },
|
|
6153
|
+
splitLine: { show: true },
|
|
6154
|
+
axisLabel: { formatter: (v) => axisFormatter(v) }
|
|
6155
|
+
},
|
|
6156
|
+
series: [
|
|
6157
|
+
{
|
|
6158
|
+
id: "box",
|
|
6159
|
+
type: "custom",
|
|
6160
|
+
encode: { x: 0, y: [1, 2, 3, 4, 5, 6, 7] },
|
|
6161
|
+
data,
|
|
6162
|
+
renderItem: boxPlotRenderer(color, entries.map((e) => e.stats?.outliers ?? []))
|
|
6163
|
+
}
|
|
6164
|
+
]
|
|
6165
|
+
};
|
|
6166
|
+
return mergeChartOptions(option, config.chartOptions);
|
|
6167
|
+
}
|
|
6168
|
+
var NO_GROUPS = [];
|
|
6169
|
+
var BoxPlotChart = forwardRef(function BoxPlotChart2({
|
|
6170
|
+
groups = NO_GROUPS,
|
|
6171
|
+
whisker,
|
|
6172
|
+
valueLabel,
|
|
6173
|
+
format,
|
|
6174
|
+
valueFormatter: valueFormatterProp,
|
|
6175
|
+
showTooltip = true,
|
|
6176
|
+
title,
|
|
6177
|
+
summary,
|
|
6178
|
+
actions,
|
|
6179
|
+
fileName,
|
|
6180
|
+
isLoading,
|
|
6181
|
+
emptyState,
|
|
6182
|
+
height,
|
|
6183
|
+
chartOptions,
|
|
6184
|
+
ariaLabel,
|
|
6185
|
+
className,
|
|
6186
|
+
...rest
|
|
6187
|
+
}, ref) {
|
|
6188
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
6189
|
+
const locale = useLocale();
|
|
6190
|
+
const t = TEXT16[uiLanguage(locale)];
|
|
6191
|
+
const valueFormatter = useMemo(() => resolveValueFormatter(valueFormatterProp, format, locale), [valueFormatterProp, format, locale]);
|
|
6192
|
+
const entries = useMemo(() => boxPlotEntries(groups, whisker), [groups, whisker]);
|
|
6193
|
+
const [color = ""] = useMemo(() => resolveChartColors(["chart-1"], tokens), [tokens]);
|
|
6194
|
+
const option = useMemo(
|
|
6195
|
+
() => buildBoxPlotOption({ entries, color, valueFormatter, valueLabel, showTooltip, labelColor: tokens?.foreground ?? void 0, chartOptions, locale }),
|
|
6196
|
+
[entries, color, valueFormatter, valueLabel, showTooltip, tokens, chartOptions, locale]
|
|
6197
|
+
);
|
|
6198
|
+
const dataTable = useMemo(() => boxPlotDataTable(entries, valueFormatter, locale), [entries, valueFormatter, locale]);
|
|
6199
|
+
const resolvedSummary = useMemo(
|
|
6200
|
+
() => summary === "auto" ? buildBoxPlotSummary(entries, valueFormatter, valueLabel, locale) : summary,
|
|
6201
|
+
[summary, entries, valueFormatter, valueLabel, locale]
|
|
6202
|
+
);
|
|
6203
|
+
const label2 = ariaLabel ?? t.chart(valueLabel ?? t.valueInSentence);
|
|
6204
|
+
return /* @__PURE__ */ jsx(
|
|
6205
|
+
ChartFrame,
|
|
6206
|
+
{
|
|
6207
|
+
...chartRootProps(rest),
|
|
6208
|
+
ref: frameRef,
|
|
6209
|
+
title,
|
|
6210
|
+
summary: resolvedSummary,
|
|
6211
|
+
isLoading: isLoading ?? false,
|
|
6212
|
+
isEmpty: entries.length === 0,
|
|
6213
|
+
emptyState,
|
|
6214
|
+
height,
|
|
6215
|
+
variant: "bar",
|
|
6216
|
+
ariaLabel: label2,
|
|
6217
|
+
className,
|
|
6218
|
+
dataTable,
|
|
6219
|
+
children: /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary: resolvedSummary } })
|
|
6220
|
+
}
|
|
6221
|
+
);
|
|
6222
|
+
});
|
|
6223
|
+
BoxPlotChart.displayName = "BoxPlotChart";
|
|
6224
|
+
var STATUS_LABELS = {
|
|
6225
|
+
ja: {
|
|
6226
|
+
success: "\u6B63\u5E38",
|
|
6227
|
+
partial: "\u4E00\u90E8\u5931\u6557",
|
|
6228
|
+
failed: "\u5931\u6557",
|
|
6229
|
+
delayed: "\u9045\u5EF6",
|
|
6230
|
+
running: "\u5B9F\u884C\u4E2D",
|
|
6231
|
+
stopped: "\u505C\u6B62\u4E2D",
|
|
6232
|
+
active: "\u6709\u52B9",
|
|
6233
|
+
inactive: "\u7121\u52B9",
|
|
6234
|
+
warning: "\u8B66\u544A",
|
|
6235
|
+
error: "\u30A8\u30E9\u30FC",
|
|
6236
|
+
pending: "\u4FDD\u7559\u4E2D"
|
|
6237
|
+
},
|
|
6238
|
+
en: {
|
|
6239
|
+
success: "Healthy",
|
|
6240
|
+
partial: "Partially failed",
|
|
6241
|
+
failed: "Failed",
|
|
6242
|
+
delayed: "Delayed",
|
|
6243
|
+
running: "Running",
|
|
6244
|
+
stopped: "Stopped",
|
|
6245
|
+
active: "Active",
|
|
6246
|
+
inactive: "Inactive",
|
|
6247
|
+
warning: "Warning",
|
|
6248
|
+
error: "Error",
|
|
6249
|
+
pending: "Pending"
|
|
6250
|
+
}
|
|
6251
|
+
};
|
|
6252
|
+
var STATUS_VOCABULARY = {
|
|
6253
|
+
success: { label: STATUS_LABELS.en.success, tone: "positive" },
|
|
6254
|
+
partial: { label: STATUS_LABELS.en.partial, tone: "warning" },
|
|
6255
|
+
failed: { label: STATUS_LABELS.en.failed, tone: "negative" },
|
|
6256
|
+
delayed: { label: STATUS_LABELS.en.delayed, tone: "warning" },
|
|
6257
|
+
running: { label: STATUS_LABELS.en.running, tone: "info" },
|
|
6258
|
+
stopped: { label: STATUS_LABELS.en.stopped, tone: "neutral" },
|
|
6259
|
+
active: { label: STATUS_LABELS.en.active, tone: "positive" },
|
|
6260
|
+
inactive: { label: STATUS_LABELS.en.inactive, tone: "neutral" },
|
|
6261
|
+
warning: { label: STATUS_LABELS.en.warning, tone: "warning" },
|
|
6262
|
+
error: { label: STATUS_LABELS.en.error, tone: "negative" },
|
|
6263
|
+
pending: { label: STATUS_LABELS.en.pending, tone: "info" }
|
|
6264
|
+
};
|
|
6265
|
+
function statusLabel(status, locale) {
|
|
6266
|
+
return Object.hasOwn(STATUS_VOCABULARY, status) ? STATUS_LABELS[uiLanguage(resolveLocale(locale))][status] : status;
|
|
6267
|
+
}
|
|
6268
|
+
var defaultColorMap = Object.fromEntries(
|
|
6269
|
+
Object.entries(STATUS_VOCABULARY).map(([key, { tone }]) => [key, tone])
|
|
6270
|
+
);
|
|
6271
|
+
var legacyColorTones = {
|
|
6272
|
+
green: "positive",
|
|
6273
|
+
gray: "neutral",
|
|
6274
|
+
yellow: "warning",
|
|
6275
|
+
red: "negative",
|
|
6276
|
+
blue: "info"
|
|
6277
|
+
};
|
|
6278
|
+
var toneStyles = {
|
|
6279
|
+
positive: { dot: "bg-positive", bg: "bg-positive-subtle", text: "text-positive" },
|
|
6280
|
+
negative: { dot: "bg-negative", bg: "bg-negative-subtle", text: "text-negative" },
|
|
6281
|
+
warning: { dot: "bg-warning", bg: "bg-warning-subtle", text: "text-warning" },
|
|
6282
|
+
info: { dot: "bg-info", bg: "bg-info-subtle", text: "text-info" },
|
|
6283
|
+
neutral: { dot: "bg-neutral", bg: "bg-neutral-subtle", text: "text-neutral" }
|
|
6284
|
+
};
|
|
6285
|
+
var resolveTone = (color) => {
|
|
6286
|
+
if (color === void 0) return "neutral";
|
|
6287
|
+
if (Object.hasOwn(toneStyles, color)) return color;
|
|
6288
|
+
return legacyColorTones[color] ?? "neutral";
|
|
6289
|
+
};
|
|
6290
|
+
var StatusBadge = forwardRef(function StatusBadge2({ status, label: label2, colorMap, className }, ref) {
|
|
6291
|
+
const locale = useLocale();
|
|
6292
|
+
const mergedColorMap = { ...defaultColorMap, ...colorMap };
|
|
6293
|
+
const style = toneStyles[resolveTone(mergedColorMap[status])];
|
|
6294
|
+
const displayLabel = label2 ?? statusLabel(status, locale);
|
|
6295
|
+
return /* @__PURE__ */ jsxs(
|
|
6296
|
+
Badge,
|
|
6297
|
+
{
|
|
6298
|
+
ref,
|
|
6299
|
+
"data-slot": "status-badge",
|
|
6300
|
+
variant: "outline",
|
|
6301
|
+
className: cn("gap-1.5 rounded-md border-transparent", style.bg, style.text, className),
|
|
6302
|
+
"aria-label": displayLabel,
|
|
6303
|
+
children: [
|
|
6304
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-block size-1.5 rounded-full", style.dot), "aria-hidden": "true" }),
|
|
6305
|
+
displayLabel
|
|
6306
|
+
]
|
|
6307
|
+
}
|
|
6308
|
+
);
|
|
6309
|
+
});
|
|
6310
|
+
StatusBadge.displayName = "StatusBadge";
|
|
6311
|
+
var KIND_ORDER = ["overdue", "risk", "dueSoon", "stale"];
|
|
6312
|
+
var KIND_TONES = {
|
|
6313
|
+
overdue: "negative",
|
|
6314
|
+
risk: "negative",
|
|
6315
|
+
dueSoon: "warning",
|
|
6316
|
+
stale: "info"
|
|
6317
|
+
};
|
|
6318
|
+
var daysEn = (n) => n === 1 ? "1 day" : `${n} days`;
|
|
6319
|
+
var TEXT17 = {
|
|
6320
|
+
ja: {
|
|
6321
|
+
labels: { overdue: "\u671F\u65E5\u8D85\u904E", risk: "\u30EA\u30B9\u30AF", dueSoon: "\u671F\u9650\u9593\u8FD1", stale: "\u6EDE\u7559" },
|
|
6322
|
+
overdue: (days) => `\u671F\u65E5\u3092 ${days} \u65E5\u904E\u304E\u3066\u3044\u307E\u3059`,
|
|
6323
|
+
dueToday: "\u671F\u65E5\u306F\u4ECA\u65E5\u3067\u3059",
|
|
6324
|
+
dueIn: (days) => `\u671F\u65E5\u307E\u3067\u3042\u3068 ${days} \u65E5\u3067\u3059`,
|
|
6325
|
+
stale: (days) => `${days} \u65E5\u9593\u52D5\u304D\u304C\u3042\u308A\u307E\u305B\u3093`,
|
|
6326
|
+
empty: "\u5BFE\u5FDC\u304C\u5FC5\u8981\u306A\u9805\u76EE\u306F\u3042\u308A\u307E\u305B\u3093",
|
|
6327
|
+
due: "\u671F\u65E5",
|
|
6328
|
+
nextAction: "\u6B21\u306E\u4E00\u624B: "
|
|
6329
|
+
},
|
|
6330
|
+
en: {
|
|
6331
|
+
labels: { overdue: "Overdue", risk: "At risk", dueSoon: "Due soon", stale: "Stalled" },
|
|
6332
|
+
overdue: (days) => `${daysEn(days)} overdue`,
|
|
6333
|
+
dueToday: "Due today",
|
|
6334
|
+
dueIn: (days) => `Due in ${daysEn(days)}`,
|
|
6335
|
+
stale: (days) => `No activity for ${daysEn(days)}`,
|
|
6336
|
+
empty: "Nothing needs attention",
|
|
6337
|
+
due: "Due",
|
|
6338
|
+
nextAction: "Next step: "
|
|
6339
|
+
}
|
|
6340
|
+
};
|
|
6341
|
+
TEXT17.en.labels;
|
|
6342
|
+
function toDate2(value) {
|
|
6343
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
6344
|
+
const date = typeof value === "string" ? parseISO(value) : value;
|
|
6345
|
+
return Number.isFinite(date.getTime()) ? date : null;
|
|
6346
|
+
}
|
|
6347
|
+
function judgeAttention(input) {
|
|
6348
|
+
const asOf = toDate2(input.asOf);
|
|
6349
|
+
if (!asOf) return null;
|
|
6350
|
+
const dueSoonDays = input.dueSoonDays ?? 7;
|
|
6351
|
+
const due = toDate2(input.dueDate);
|
|
6352
|
+
if (due) {
|
|
6353
|
+
const untilDue = differenceInCalendarDays(due, asOf);
|
|
6354
|
+
if (untilDue < 0) return { kind: "overdue", days: -untilDue };
|
|
6355
|
+
}
|
|
6356
|
+
if (input.risk) return { kind: "risk", days: 0 };
|
|
6357
|
+
if (due) {
|
|
6358
|
+
const untilDue = differenceInCalendarDays(due, asOf);
|
|
6359
|
+
if (untilDue <= dueSoonDays) return { kind: "dueSoon", days: untilDue };
|
|
6360
|
+
}
|
|
6361
|
+
const last = toDate2(input.lastActivityAt);
|
|
6362
|
+
if (last && input.staleDays !== void 0) {
|
|
6363
|
+
const idle = differenceInCalendarDays(asOf, last);
|
|
6364
|
+
if (idle >= input.staleDays) return { kind: "stale", days: idle };
|
|
6365
|
+
}
|
|
6366
|
+
return null;
|
|
6367
|
+
}
|
|
6368
|
+
function compareAttention(a, b) {
|
|
6369
|
+
if (!a || !b) return a ? -1 : b ? 1 : 0;
|
|
6370
|
+
const byKind = KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind);
|
|
6371
|
+
if (byKind !== 0) return byKind;
|
|
6372
|
+
return a.kind === "dueSoon" ? a.days - b.days : b.days - a.days;
|
|
6373
|
+
}
|
|
6374
|
+
function defaultReason(j, labels, t) {
|
|
6375
|
+
switch (j.kind) {
|
|
6376
|
+
case "overdue":
|
|
6377
|
+
return t.overdue(j.days);
|
|
6378
|
+
case "dueSoon":
|
|
6379
|
+
return j.days === 0 ? t.dueToday : t.dueIn(j.days);
|
|
6380
|
+
case "stale":
|
|
6381
|
+
return t.stale(j.days);
|
|
6382
|
+
case "risk":
|
|
6383
|
+
return labels.risk;
|
|
6384
|
+
}
|
|
6385
|
+
}
|
|
6386
|
+
function cell(row, key) {
|
|
6387
|
+
if (!key || row === null || typeof row !== "object") return void 0;
|
|
6388
|
+
return row[key];
|
|
6389
|
+
}
|
|
6390
|
+
function asDateInput(value) {
|
|
6391
|
+
return typeof value === "string" || value instanceof Date ? value : void 0;
|
|
6392
|
+
}
|
|
6393
|
+
function asNode(value) {
|
|
6394
|
+
return typeof value === "string" || typeof value === "number" ? value : null;
|
|
6395
|
+
}
|
|
6396
|
+
function AttentionList({
|
|
6397
|
+
rows,
|
|
6398
|
+
asOf,
|
|
6399
|
+
dueSoonDays,
|
|
6400
|
+
staleDays,
|
|
6401
|
+
labels,
|
|
6402
|
+
getRowId,
|
|
6403
|
+
getTitle,
|
|
6404
|
+
getDueDate,
|
|
6405
|
+
getLastActivityAt,
|
|
6406
|
+
isRisk,
|
|
6407
|
+
idKey,
|
|
6408
|
+
titleKey,
|
|
6409
|
+
dueDateKey,
|
|
6410
|
+
lastActivityAtKey,
|
|
6411
|
+
riskKey,
|
|
6412
|
+
nextActionKey,
|
|
6413
|
+
getReason,
|
|
6414
|
+
getNextAction,
|
|
6415
|
+
getMeta,
|
|
6416
|
+
onRowClick,
|
|
6417
|
+
includeUnflagged = false,
|
|
6418
|
+
emptyTitle,
|
|
6419
|
+
emptyDescription,
|
|
6420
|
+
className,
|
|
6421
|
+
...rest
|
|
6422
|
+
}) {
|
|
6423
|
+
const locale = useLocale();
|
|
6424
|
+
const t = TEXT17[uiLanguage(locale)];
|
|
6425
|
+
const mergedLabels = useMemo(() => ({ ...t.labels, ...labels }), [t, labels]);
|
|
6426
|
+
const colorMap = KIND_TONES;
|
|
6427
|
+
const dueOf = (row) => getDueDate ? getDueDate(row) : asDateInput(cell(row, dueDateKey));
|
|
6428
|
+
const nextActionOf = (row, j) => getNextAction ? getNextAction(row, j) : asNode(cell(row, nextActionKey));
|
|
6429
|
+
const items = (rows ?? []).map((row, index) => ({
|
|
6430
|
+
row,
|
|
6431
|
+
index,
|
|
6432
|
+
judgement: judgeAttention({
|
|
6433
|
+
dueDate: dueOf(row),
|
|
6434
|
+
lastActivityAt: getLastActivityAt ? getLastActivityAt(row) : asDateInput(cell(row, lastActivityAtKey)),
|
|
6435
|
+
risk: isRisk ? isRisk(row) : Boolean(cell(row, riskKey)),
|
|
6436
|
+
asOf,
|
|
6437
|
+
dueSoonDays,
|
|
6438
|
+
staleDays
|
|
6439
|
+
})
|
|
6440
|
+
})).filter((item) => includeUnflagged || item.judgement).sort((a, b) => compareAttention(a.judgement, b.judgement) || a.index - b.index);
|
|
6441
|
+
if (items.length === 0) {
|
|
6442
|
+
return /* @__PURE__ */ jsx(Empty, { title: emptyTitle ?? t.empty, description: emptyDescription, className });
|
|
6443
|
+
}
|
|
6444
|
+
return /* @__PURE__ */ jsx("ul", { "data-slot": "attention-list", className: cn("divide-y divide-border", className), ...rest, children: items.map(({ row, index, judgement }) => {
|
|
6445
|
+
const due = dueOf(row);
|
|
6446
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6447
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1", children: [
|
|
6448
|
+
judgement && /* @__PURE__ */ jsx(StatusBadge, { status: judgement.kind, label: mergedLabels[judgement.kind], colorMap }),
|
|
6449
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 font-medium", children: getTitle ? getTitle(row) : asNode(cell(row, titleKey)) }),
|
|
6450
|
+
getMeta && /* @__PURE__ */ jsx("span", { className: "text-sm tabular-nums text-muted-foreground", children: getMeta(row) }),
|
|
6451
|
+
due ? /* @__PURE__ */ jsxs("span", { className: "ml-auto text-sm tabular-nums text-muted-foreground", children: [
|
|
6452
|
+
t.due,
|
|
6453
|
+
" ",
|
|
6454
|
+
formatDate(due, "date", locale)
|
|
6455
|
+
] }) : null
|
|
6456
|
+
] }),
|
|
6457
|
+
judgement && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: getReason ? getReason(row, judgement) : defaultReason(judgement, mergedLabels, t) }),
|
|
6458
|
+
judgement && (getNextAction || nextActionKey) && nextActionOf(row, judgement) ? /* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm", children: [
|
|
6459
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t.nextAction }),
|
|
6460
|
+
nextActionOf(row, judgement)
|
|
6461
|
+
] }) : null
|
|
6462
|
+
] });
|
|
6463
|
+
return /* @__PURE__ */ jsx("li", { "data-slot": "attention-list-row", children: onRowClick ? /* @__PURE__ */ jsx(
|
|
6464
|
+
"button",
|
|
6465
|
+
{
|
|
6466
|
+
type: "button",
|
|
6467
|
+
onClick: () => onRowClick(row),
|
|
6468
|
+
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",
|
|
6469
|
+
children: body
|
|
6470
|
+
}
|
|
6471
|
+
) : /* @__PURE__ */ jsx("div", { className: "px-3 py-3", children: body }) }, getRowId ? getRowId(row) : String(cell(row, idKey) ?? index));
|
|
6472
|
+
}) });
|
|
6473
|
+
}
|
|
6474
|
+
AttentionList.displayName = "AttentionList";
|
|
6475
|
+
function niceStep2(raw) {
|
|
6476
|
+
if (!(raw > 0) || !Number.isFinite(raw)) return 1;
|
|
6477
|
+
const exp10 = 10 ** Math.floor(Math.log10(raw));
|
|
6478
|
+
const f = raw / exp10;
|
|
6479
|
+
return (f < 1.5 ? 1 : f < 2.5 ? 2 : f < 4 ? 3 : f < 7 ? 5 : 10) * exp10;
|
|
6480
|
+
}
|
|
6481
|
+
var round = (n) => Number(n.toPrecision(12));
|
|
6482
|
+
function comboAxisScale(lo, hi, splitNumber, ticks) {
|
|
6483
|
+
let low = Math.min(0, Number.isFinite(lo) ? lo : 0);
|
|
6484
|
+
let high = Math.max(0, Number.isFinite(hi) ? hi : 0);
|
|
6485
|
+
if (high <= low) high = low + 1;
|
|
6486
|
+
let step = niceStep2((high - low) / Math.max(1, splitNumber));
|
|
6487
|
+
for (let attempt = 0; ; attempt++) {
|
|
6488
|
+
const min = round(Math.floor(low / step) * step);
|
|
6489
|
+
const max = round(Math.ceil(high / step) * step);
|
|
6490
|
+
const tick = ticks(Math.max(Math.abs(min), Math.abs(max)));
|
|
6491
|
+
const count = Math.round((max - min) / step);
|
|
6492
|
+
const labels = Array.from({ length: count + 1 }, (_, i) => tick(round(min + i * step)));
|
|
6493
|
+
if (new Set(labels).size === labels.length || attempt >= 6) return { min, max, interval: step, tick };
|
|
6494
|
+
step = niceStep2(step * 2);
|
|
6495
|
+
}
|
|
6496
|
+
}
|
|
6497
|
+
var DEFAULT_CHART_HEIGHT2 = 288;
|
|
6498
|
+
var splitCountFor = (height) => height < 200 ? 3 : height < 300 ? 4 : 5;
|
|
6499
|
+
function escapeHtml3(text) {
|
|
6500
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
6501
|
+
}
|
|
6502
|
+
var TEXT18 = {
|
|
6503
|
+
ja: { item: "\u9805\u76EE", chart: "\u8907\u5408\u30B0\u30E9\u30D5" },
|
|
6504
|
+
en: { item: "Item", chart: "Combination chart" }
|
|
6505
|
+
};
|
|
6506
|
+
var textOf6 = (locale) => TEXT18[uiLanguage(resolveLocale(locale))];
|
|
6507
|
+
var axisOf = (s) => s.axis === "right" ? "right" : "left";
|
|
6508
|
+
function comboSeriesList(series) {
|
|
6509
|
+
if (!Array.isArray(series)) return [];
|
|
6510
|
+
return series.filter(
|
|
6511
|
+
(s) => !!s && typeof s === "object" && typeof s.key === "string" && ["bar", "line", "area"].includes(s.type)
|
|
6512
|
+
);
|
|
6513
|
+
}
|
|
6514
|
+
function buildComboChartOption(config) {
|
|
6515
|
+
const { data, index, labels, formatters } = config;
|
|
6516
|
+
const series = comboSeriesList(config.series);
|
|
6517
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
6518
|
+
const seriesName = (s) => s.label ?? label2(s.key);
|
|
6519
|
+
const vertical = config.layout === "vertical";
|
|
6520
|
+
const categories = data.map((row) => String(row?.[index] ?? ""));
|
|
6521
|
+
const values = series.map((s) => data.map((row) => toChartNumber(row?.[s.key])));
|
|
6522
|
+
const hasRight = series.some((s) => axisOf(s) === "right");
|
|
6523
|
+
const axes = hasRight ? ["left", "right"] : ["left"];
|
|
6524
|
+
const extent = (axis) => {
|
|
6525
|
+
let [lo, hi] = [0, 0];
|
|
6526
|
+
const add = (v) => {
|
|
6527
|
+
if (v !== null && Number.isFinite(v)) [lo, hi] = [Math.min(lo, v), Math.max(hi, v)];
|
|
6528
|
+
};
|
|
6529
|
+
const stacks = /* @__PURE__ */ new Map();
|
|
6530
|
+
series.forEach((s, i) => {
|
|
6531
|
+
if (axisOf(s) !== axis) return;
|
|
6532
|
+
if (config.stacked && s.type !== "line") {
|
|
6533
|
+
const stack = stacks.get(s.type) ?? { pos: data.map(() => 0), neg: data.map(() => 0) };
|
|
6534
|
+
values[i].forEach((v, j) => {
|
|
6535
|
+
if (v === null) return;
|
|
6536
|
+
if (v >= 0) stack.pos[j] += v;
|
|
6537
|
+
else stack.neg[j] += v;
|
|
6538
|
+
});
|
|
6539
|
+
stacks.set(s.type, stack);
|
|
6540
|
+
} else values[i].forEach(add);
|
|
6541
|
+
});
|
|
6542
|
+
for (const stack of stacks.values()) [...stack.pos, ...stack.neg].forEach(add);
|
|
6543
|
+
for (const ref of config.referenceLines ?? []) if (axisOf(ref) === axis) add(toChartNumber(ref.value));
|
|
6544
|
+
return [lo, hi];
|
|
6545
|
+
};
|
|
6546
|
+
const split = splitCountFor(config.plotHeight ?? DEFAULT_CHART_HEIGHT2);
|
|
6547
|
+
const scales = Object.fromEntries(axes.map((axis) => [axis, comboAxisScale(...extent(axis), split, config.ticks[axis])]));
|
|
6548
|
+
const valueAxes = axes.map((axis) => ({
|
|
6549
|
+
type: "value",
|
|
6550
|
+
position: vertical ? axis === "left" ? "bottom" : "top" : axis,
|
|
6551
|
+
min: scales[axis].min,
|
|
6552
|
+
max: scales[axis].max,
|
|
6553
|
+
interval: scales[axis].interval,
|
|
6554
|
+
axisLine: { show: false },
|
|
6555
|
+
axisTick: { show: false },
|
|
6556
|
+
splitLine: { show: axis === "left" },
|
|
6557
|
+
axisLabel: { formatter: (v) => scales[axis].tick(v), hideOverlap: vertical ? true : void 0 }
|
|
6558
|
+
}));
|
|
6559
|
+
const categoryAxis = {
|
|
6560
|
+
type: "category",
|
|
6561
|
+
data: categories,
|
|
6562
|
+
inverse: vertical,
|
|
6563
|
+
axisTick: { show: false },
|
|
6564
|
+
axisLabel: { formatter: (v) => label2(v), hideOverlap: true }
|
|
6565
|
+
};
|
|
6566
|
+
const axisIndex = (s) => axisOf(s) === "right" ? 1 : 0;
|
|
6567
|
+
const drawn = series.map((s, i) => {
|
|
6568
|
+
const color = config.palette[i] || void 0;
|
|
6569
|
+
const common = {
|
|
6570
|
+
id: `series-${i}`,
|
|
6571
|
+
name: seriesName(s),
|
|
6572
|
+
data: values[i],
|
|
6573
|
+
[vertical ? "xAxisIndex" : "yAxisIndex"]: axisIndex(s),
|
|
6574
|
+
emphasis: { focus: "none" }
|
|
6575
|
+
};
|
|
6576
|
+
if (s.type === "bar")
|
|
6577
|
+
return {
|
|
6578
|
+
...common,
|
|
6579
|
+
type: "bar",
|
|
6580
|
+
stack: config.stacked ? `bar-${axisOf(s)}` : void 0,
|
|
6581
|
+
barMaxWidth: 40,
|
|
6582
|
+
itemStyle: { color, borderRadius: vertical ? [0, 2, 2, 0] : [2, 2, 0, 0] }
|
|
6583
|
+
};
|
|
6584
|
+
return {
|
|
6585
|
+
...common,
|
|
6586
|
+
type: "line",
|
|
6587
|
+
stack: config.stacked && s.type === "area" ? `area-${axisOf(s)}` : void 0,
|
|
6588
|
+
symbol: "circle",
|
|
6589
|
+
symbolSize: 6,
|
|
6590
|
+
showSymbol: data.length <= 24,
|
|
6591
|
+
connectNulls: false,
|
|
6592
|
+
lineStyle: { width: 2, color },
|
|
6593
|
+
itemStyle: { color },
|
|
6594
|
+
areaStyle: s.type === "area" ? { color, opacity: 0.18 } : void 0,
|
|
6595
|
+
z: 3
|
|
6596
|
+
};
|
|
6597
|
+
});
|
|
6598
|
+
const references = axes.flatMap((axis) => {
|
|
6599
|
+
const lines = (config.referenceLines ?? []).filter((r) => axisOf(r) === axis && toChartNumber(r.value) !== null);
|
|
6600
|
+
if (lines.length === 0) return [];
|
|
6601
|
+
return [
|
|
6602
|
+
{
|
|
6603
|
+
id: `reference-${axis}`,
|
|
6604
|
+
type: "line",
|
|
6605
|
+
data: data.map(() => null),
|
|
6606
|
+
[vertical ? "xAxisIndex" : "yAxisIndex"]: axis === "right" ? 1 : 0,
|
|
6607
|
+
silent: true,
|
|
6608
|
+
symbol: "none",
|
|
6609
|
+
tooltip: { show: false },
|
|
6610
|
+
markLine: {
|
|
6611
|
+
silent: true,
|
|
6612
|
+
symbol: "none",
|
|
6613
|
+
lineStyle: { type: "dashed", color: config.referenceColor || void 0, width: 1 },
|
|
6614
|
+
label: { position: "insideEndTop", color: config.referenceColor || void 0 },
|
|
6615
|
+
data: lines.map((r) => ({
|
|
6616
|
+
[vertical ? "xAxis" : "yAxis"]: toChartNumber(r.value),
|
|
6617
|
+
label: { formatter: r.label ?? config.formatters[axis](toChartNumber(r.value)) }
|
|
6618
|
+
}))
|
|
6619
|
+
}
|
|
6620
|
+
}
|
|
6621
|
+
];
|
|
6622
|
+
});
|
|
6623
|
+
const showLegend = (config.showLegend ?? true) && series.length > 1;
|
|
6624
|
+
const tooltip = (params) => {
|
|
6625
|
+
const items = Array.isArray(params) ? params : [params];
|
|
6626
|
+
const first = items[0];
|
|
6627
|
+
if (!first) return "";
|
|
6628
|
+
const head = escapeHtml3(label2(categories[first.dataIndex] ?? ""));
|
|
6629
|
+
const lines = items.filter((p) => p.seriesIndex < series.length).map((p) => {
|
|
6630
|
+
const s = series[p.seriesIndex];
|
|
6631
|
+
const v = values[p.seriesIndex][p.dataIndex];
|
|
6632
|
+
return `${p.marker ?? ""}${escapeHtml3(seriesName(s))} ${v === null || v === void 0 ? EMPTY_VALUE : escapeHtml3(formatters[axisOf(s)](v))}`;
|
|
6633
|
+
});
|
|
6634
|
+
return [head, ...lines].join("<br/>");
|
|
6635
|
+
};
|
|
6636
|
+
const option = {
|
|
6637
|
+
animation: false,
|
|
6638
|
+
legend: { show: showLegend, top: 0, left: 0, data: series.map(seriesName) },
|
|
6639
|
+
tooltip: config.showTooltip === false ? { show: false } : { trigger: "axis", axisPointer: { type: "shadow" }, formatter: tooltip },
|
|
6640
|
+
grid: { left: 8, right: 8, top: showLegend ? 36 : 12, bottom: 8, containLabel: true },
|
|
6641
|
+
xAxis: vertical ? valueAxes : categoryAxis,
|
|
6642
|
+
yAxis: vertical ? categoryAxis : valueAxes,
|
|
6643
|
+
series: [...drawn, ...references]
|
|
6644
|
+
};
|
|
6645
|
+
return config.chartOptions ? mergeDeep2(option, config.chartOptions) : option;
|
|
6646
|
+
}
|
|
6647
|
+
function mergeDeep2(a, b) {
|
|
6648
|
+
const out = { ...a };
|
|
6649
|
+
for (const [k, v] of Object.entries(b)) {
|
|
6650
|
+
const cur = out[k];
|
|
6651
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep2(cur, v) : v;
|
|
6652
|
+
}
|
|
6653
|
+
return out;
|
|
6654
|
+
}
|
|
6655
|
+
function comboChartDataTable(data, index, series, formatters, labels, locale) {
|
|
6656
|
+
const list = comboSeriesList(series);
|
|
6657
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
6658
|
+
return {
|
|
6659
|
+
columns: [labels?.[index] ?? textOf6(locale).item, ...list.map((s) => s.label ?? label2(s.key))],
|
|
6660
|
+
rows: data.map((row) => [
|
|
6661
|
+
label2(String(row?.[index] ?? "")),
|
|
6662
|
+
...list.map((s) => {
|
|
6663
|
+
const n = toChartNumber(row?.[s.key]);
|
|
6664
|
+
return n === null ? EMPTY_VALUE : formatters[axisOf(s)](n);
|
|
6665
|
+
})
|
|
6666
|
+
])
|
|
6667
|
+
};
|
|
6668
|
+
}
|
|
6669
|
+
var NO_ROWS13 = [];
|
|
6670
|
+
function ComboChartImpl({
|
|
6671
|
+
data = NO_ROWS13,
|
|
6672
|
+
index,
|
|
6673
|
+
series = NO_ROWS13,
|
|
6674
|
+
format,
|
|
6675
|
+
rightFormat,
|
|
6676
|
+
labels,
|
|
6677
|
+
referenceLines,
|
|
6678
|
+
stacked = false,
|
|
6679
|
+
layout = "horizontal",
|
|
6680
|
+
showLegend = true,
|
|
6681
|
+
showTooltip = true,
|
|
6682
|
+
onSelect,
|
|
6683
|
+
title,
|
|
6684
|
+
summary,
|
|
6685
|
+
isLoading,
|
|
6686
|
+
emptyState,
|
|
6687
|
+
height,
|
|
6688
|
+
chartOptions,
|
|
6689
|
+
actions,
|
|
6690
|
+
fileName,
|
|
6691
|
+
className,
|
|
6692
|
+
ariaLabel,
|
|
6693
|
+
...rest
|
|
6694
|
+
}, ref) {
|
|
6695
|
+
const rows = Array.isArray(data) ? data : NO_ROWS13;
|
|
6696
|
+
const list = useMemo(() => comboSeriesList(series), [series]);
|
|
6697
|
+
const { ref: frameRef, tokens } = useChartSurface(ref);
|
|
6698
|
+
const locale = useLocale();
|
|
6699
|
+
const formatters = useMemo(
|
|
6700
|
+
() => ({ left: resolveValueFormatter(void 0, format, locale), right: resolveValueFormatter(void 0, rightFormat ?? format, locale) }),
|
|
6701
|
+
[format, rightFormat, locale]
|
|
6702
|
+
);
|
|
6703
|
+
const ticks = useMemo(
|
|
6704
|
+
() => ({ left: resolveAxisTickFormatter(void 0, format, locale), right: resolveAxisTickFormatter(void 0, rightFormat ?? format, locale) }),
|
|
6705
|
+
[format, rightFormat, locale]
|
|
6706
|
+
);
|
|
6707
|
+
const palette = useMemo(() => resolveColors(tokens, list), [tokens, list]);
|
|
6708
|
+
const option = useMemo(
|
|
6709
|
+
() => buildComboChartOption({
|
|
6710
|
+
data: rows,
|
|
6711
|
+
index,
|
|
6712
|
+
series: list,
|
|
6713
|
+
palette: palette.series,
|
|
6714
|
+
referenceColor: palette.reference,
|
|
6715
|
+
labels,
|
|
6716
|
+
formatters,
|
|
6717
|
+
ticks,
|
|
6718
|
+
referenceLines,
|
|
6719
|
+
stacked,
|
|
6720
|
+
layout,
|
|
6721
|
+
showLegend,
|
|
6722
|
+
showTooltip,
|
|
6723
|
+
plotHeight: height,
|
|
6724
|
+
chartOptions
|
|
6725
|
+
}),
|
|
6726
|
+
[rows, index, list, palette, labels, formatters, ticks, referenceLines, stacked, layout, showLegend, showTooltip, height, chartOptions]
|
|
6727
|
+
);
|
|
6728
|
+
const onEvents = useMemo(
|
|
6729
|
+
() => onSelect ? {
|
|
6730
|
+
click: (params) => {
|
|
6731
|
+
const p = params;
|
|
6732
|
+
if (p.componentType !== "series" || (p.seriesIndex ?? Infinity) >= list.length) return;
|
|
6733
|
+
const row = rows[p.dataIndex ?? -1];
|
|
6734
|
+
if (row) onSelect(row);
|
|
6735
|
+
}
|
|
6736
|
+
} : void 0,
|
|
6737
|
+
[onSelect, rows, list.length]
|
|
6738
|
+
);
|
|
6739
|
+
const dataTable = useMemo(() => comboChartDataTable(rows, index, list, formatters, labels, locale), [rows, index, list, formatters, labels, locale]);
|
|
6740
|
+
const label2 = ariaLabel ?? textOf6(locale).chart;
|
|
6741
|
+
return /* @__PURE__ */ jsx(
|
|
6742
|
+
ChartFrame,
|
|
6743
|
+
{
|
|
6744
|
+
...chartRootProps(rest),
|
|
6745
|
+
ref: frameRef,
|
|
6746
|
+
title,
|
|
6747
|
+
summary,
|
|
6748
|
+
isLoading: isLoading ?? false,
|
|
6749
|
+
isEmpty: rows.length === 0,
|
|
6750
|
+
emptyState,
|
|
6751
|
+
height,
|
|
6752
|
+
variant: "bar",
|
|
6753
|
+
ariaLabel: label2,
|
|
6754
|
+
className,
|
|
6755
|
+
dataTable,
|
|
6756
|
+
children: /* @__PURE__ */ jsx(EChart, { option, onEvents, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary } })
|
|
6757
|
+
}
|
|
6758
|
+
);
|
|
6759
|
+
}
|
|
6760
|
+
function resolveColors(tokens, series) {
|
|
6761
|
+
const resolved = resolveChartColors(
|
|
6762
|
+
["var(--muted-foreground)", ...series.map((s, i) => s.color ?? DEFAULT_CHART_COLORS[i % DEFAULT_CHART_COLORS.length])],
|
|
6763
|
+
tokens
|
|
6764
|
+
);
|
|
6765
|
+
return { reference: resolved[0] ?? "", series: resolved.slice(1) };
|
|
6766
|
+
}
|
|
6767
|
+
var ComboChart = forwardRef(ComboChartImpl);
|
|
6768
|
+
ComboChart.displayName = "ComboChart";
|
|
6769
|
+
|
|
6770
|
+
// ../squadkit/registry/lib/forecast.ts
|
|
6771
|
+
var FORECAST_MIN_POINTS = 5;
|
|
6772
|
+
var FORECAST_MAX_FIT_POINTS = 1e3;
|
|
6773
|
+
function inverseNormal(p) {
|
|
6774
|
+
if (!(p > 0 && p < 1)) return p <= 0 ? -Infinity : Infinity;
|
|
6775
|
+
const a = [-39.69683028665376, 220.9460984245205, -275.9285104469687, 138.357751867269, -30.66479806614716, 2.506628277459239];
|
|
6776
|
+
const b = [-54.47609879822406, 161.5858368580409, -155.6989798598866, 66.80131188771972, -13.28068155288572];
|
|
6777
|
+
const c = [-0.007784894002430293, -0.3223964580411365, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783];
|
|
6778
|
+
const d = [0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416];
|
|
6779
|
+
const low = 0.02425;
|
|
6780
|
+
if (p < low) {
|
|
6781
|
+
const q2 = Math.sqrt(-2 * Math.log(p));
|
|
6782
|
+
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);
|
|
6783
|
+
}
|
|
6784
|
+
if (p > 1 - low) {
|
|
6785
|
+
const q2 = Math.sqrt(-2 * Math.log(1 - p));
|
|
6786
|
+
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);
|
|
6787
|
+
}
|
|
6788
|
+
const q = p - 0.5;
|
|
6789
|
+
const r = q * q;
|
|
6790
|
+
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);
|
|
6791
|
+
}
|
|
6792
|
+
function nelderMead(f, start, step, maxEvals) {
|
|
6793
|
+
const dim = start.length;
|
|
6794
|
+
const simplex = [{ x: start.slice(), fx: f(start) }];
|
|
6795
|
+
for (let i = 0; i < dim; i++) {
|
|
6796
|
+
const x = start.slice();
|
|
6797
|
+
x[i] = x[i] + step;
|
|
6798
|
+
simplex.push({ x, fx: f(x) });
|
|
6799
|
+
}
|
|
6800
|
+
let evals = dim + 1;
|
|
6801
|
+
const combine = (a, b, t) => a.map((v, i) => v + t * (b[i] - v));
|
|
6802
|
+
while (evals < maxEvals) {
|
|
6803
|
+
simplex.sort((p, q) => p.fx - q.fx);
|
|
6804
|
+
const best = simplex[0];
|
|
6805
|
+
const worst = simplex[dim];
|
|
6806
|
+
const spread = Math.abs(worst.fx - best.fx);
|
|
6807
|
+
if (spread <= 1e-10 * (Math.abs(best.fx) + 1e-20)) break;
|
|
6808
|
+
const centroid = new Array(dim).fill(0);
|
|
6809
|
+
for (let i = 0; i < dim; i++) for (let j = 0; j < dim; j++) centroid[j] += simplex[i].x[j] / dim;
|
|
6810
|
+
const reflected = combine(centroid, worst.x, -1);
|
|
6811
|
+
const fr = f(reflected);
|
|
6812
|
+
evals++;
|
|
6813
|
+
if (fr < best.fx) {
|
|
6814
|
+
const expanded = combine(centroid, worst.x, -2);
|
|
6815
|
+
const fe = f(expanded);
|
|
6816
|
+
evals++;
|
|
6817
|
+
simplex[dim] = fe < fr ? { x: expanded, fx: fe } : { x: reflected, fx: fr };
|
|
6818
|
+
continue;
|
|
6819
|
+
}
|
|
6820
|
+
if (fr < simplex[dim - 1].fx) {
|
|
6821
|
+
simplex[dim] = { x: reflected, fx: fr };
|
|
6822
|
+
continue;
|
|
6823
|
+
}
|
|
6824
|
+
const outside = fr < worst.fx;
|
|
6825
|
+
const contracted = outside ? combine(centroid, reflected, 0.5) : combine(centroid, worst.x, 0.5);
|
|
6826
|
+
const fc = f(contracted);
|
|
6827
|
+
evals++;
|
|
6828
|
+
if (fc < (outside ? fr : worst.fx)) {
|
|
6829
|
+
simplex[dim] = { x: contracted, fx: fc };
|
|
6830
|
+
continue;
|
|
6831
|
+
}
|
|
6832
|
+
for (let i = 1; i <= dim; i++) {
|
|
6833
|
+
const x = combine(best.x, simplex[i].x, 0.5);
|
|
6834
|
+
simplex[i] = { x, fx: f(x) };
|
|
6835
|
+
evals++;
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
simplex.sort((p, q) => p.fx - q.fx);
|
|
6839
|
+
return simplex[0];
|
|
6840
|
+
}
|
|
6841
|
+
function run(y, spec, p) {
|
|
6842
|
+
let l = p.l0;
|
|
6843
|
+
let b = spec.trend === "additive" ? p.b0 : 0;
|
|
6844
|
+
const s = spec.season === "none" ? [] : p.s0.slice();
|
|
6845
|
+
const mult = spec.season === "multiplicative";
|
|
6846
|
+
let sse = 0;
|
|
6847
|
+
for (let t = 0; t < y.length; t++) {
|
|
6848
|
+
const j = t % spec.m;
|
|
6849
|
+
const base = l + b;
|
|
6850
|
+
const sj = spec.season === "none" ? mult ? 1 : 0 : s[j];
|
|
6851
|
+
if (mult && (sj <= 0 || base <= 0)) return null;
|
|
6852
|
+
const yhat = mult ? base * sj : base + sj;
|
|
6853
|
+
const e = y[t] - yhat;
|
|
6854
|
+
if (!Number.isFinite(e)) return null;
|
|
6855
|
+
sse += e * e;
|
|
6856
|
+
if (mult) {
|
|
6857
|
+
l = base + p.alpha * e / sj;
|
|
6858
|
+
if (spec.trend === "additive") b = b + p.beta * e / sj;
|
|
6859
|
+
s[j] = sj + p.gamma * e / base;
|
|
6860
|
+
} else {
|
|
6861
|
+
l = base + p.alpha * e;
|
|
6862
|
+
if (spec.trend === "additive") b = b + p.beta * e;
|
|
6863
|
+
if (spec.season === "additive") s[j] = sj + p.gamma * e;
|
|
6864
|
+
}
|
|
6865
|
+
}
|
|
6866
|
+
return Number.isFinite(sse) ? { sse, level: l, slope: b, season: s } : null;
|
|
6867
|
+
}
|
|
6868
|
+
function initialSeason(y, m, mult) {
|
|
6869
|
+
const len = Math.min(Math.floor(y.length / m), 3) * m;
|
|
6870
|
+
const half = Math.floor(m / 2);
|
|
6871
|
+
const sums = new Array(m).fill(0);
|
|
6872
|
+
const counts = new Array(m).fill(0);
|
|
6873
|
+
for (let t = half; t < len - half; t++) {
|
|
6874
|
+
let trend;
|
|
6875
|
+
if (m % 2 === 0) {
|
|
6876
|
+
let sum = 0.5 * y[t - half] + 0.5 * y[t + half];
|
|
6877
|
+
for (let k = t - half + 1; k < t + half; k++) sum += y[k];
|
|
6878
|
+
trend = sum / m;
|
|
6879
|
+
} else {
|
|
6880
|
+
let sum = 0;
|
|
6881
|
+
for (let k = t - half; k <= t + half; k++) sum += y[k];
|
|
6882
|
+
trend = sum / m;
|
|
6883
|
+
}
|
|
6884
|
+
if (mult && trend <= 0) continue;
|
|
6885
|
+
sums[t % m] += mult ? y[t] / trend : y[t] - trend;
|
|
6886
|
+
counts[t % m]++;
|
|
6887
|
+
}
|
|
6888
|
+
const raw = sums.map((v, j) => counts[j] > 0 ? v / counts[j] : mult ? 1 : 0);
|
|
6889
|
+
const mean = raw.reduce((a, v) => a + v, 0) / m;
|
|
6890
|
+
return mult ? raw.map((v) => mean > 0 ? v / mean : 1) : raw.map((v) => v - mean);
|
|
6891
|
+
}
|
|
6892
|
+
function linearFit(z) {
|
|
6893
|
+
const n = z.length;
|
|
6894
|
+
if (n < 2) return { intercept: z[0] ?? 0, slope: 0 };
|
|
6895
|
+
const mt = (n - 1) / 2;
|
|
6896
|
+
const mz = z.reduce((a, v) => a + v, 0) / n;
|
|
6897
|
+
let num = 0;
|
|
6898
|
+
let den = 0;
|
|
6899
|
+
z.forEach((v, t) => {
|
|
6900
|
+
num += (t - mt) * (v - mz);
|
|
6901
|
+
den += (t - mt) * (t - mt);
|
|
6902
|
+
});
|
|
6903
|
+
const slope = den === 0 ? 0 : num / den;
|
|
6904
|
+
return { intercept: mz - slope * mt, slope };
|
|
6905
|
+
}
|
|
6906
|
+
var logistic = (x) => 1 / (1 + Math.exp(-x));
|
|
6907
|
+
var logit = (p) => Math.log(p / (1 - p));
|
|
6908
|
+
var EDGE = 1e-4;
|
|
6909
|
+
function fitModel(y, spec) {
|
|
6910
|
+
const n = y.length;
|
|
6911
|
+
const { m } = spec;
|
|
6912
|
+
const seasonal = spec.season !== "none";
|
|
6913
|
+
const mult = spec.season === "multiplicative";
|
|
6914
|
+
const s0 = seasonal ? initialSeason(y, m, mult) : [];
|
|
6915
|
+
const adjusted = y.map((v, t) => !seasonal ? v : mult ? v / s0[t % m] : v - s0[t % m]);
|
|
6916
|
+
const head = adjusted.slice(0, seasonal ? Math.min(n, Math.max(2 * m, 10)) : Math.min(n, 10));
|
|
6917
|
+
const line = linearFit(head);
|
|
6918
|
+
const trend = spec.trend === "additive";
|
|
6919
|
+
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);
|
|
6920
|
+
const b0Init = trend ? line.slope : 0;
|
|
6921
|
+
const mean = y.reduce((a, v) => a + v, 0) / n;
|
|
6922
|
+
const sd = Math.sqrt(y.reduce((a, v) => a + (v - mean) * (v - mean), 0) / n);
|
|
6923
|
+
const scale = Math.max(sd, Math.abs(mean) * 1e-3, 1e-9);
|
|
6924
|
+
const decode = (x) => {
|
|
6925
|
+
let i = 0;
|
|
6926
|
+
const alpha = EDGE + (1 - 2 * EDGE) * logistic(x[i++]);
|
|
6927
|
+
const beta = trend ? alpha * (EDGE + (1 - 2 * EDGE) * logistic(x[i++])) : 0;
|
|
6928
|
+
const gamma = seasonal ? (1 - alpha) * (EDGE + (1 - 2 * EDGE) * logistic(x[i++])) : 0;
|
|
6929
|
+
const l0 = l0Init + x[i++] * scale;
|
|
6930
|
+
const b0 = trend ? b0Init + x[i++] * scale * 0.1 : 0;
|
|
6931
|
+
return { alpha, beta, gamma, l0, b0, s0 };
|
|
6932
|
+
};
|
|
6933
|
+
const objective = (x) => run(y, spec, decode(x))?.sse ?? Infinity;
|
|
6934
|
+
const starts = [
|
|
6935
|
+
[0.2, 0.1, 0.1],
|
|
6936
|
+
[0.5, 0.3, 0.3],
|
|
6937
|
+
[0.05, 0.5, 0.05]
|
|
6938
|
+
];
|
|
6939
|
+
let best = null;
|
|
6940
|
+
for (const [a, b, g] of starts) {
|
|
6941
|
+
const x0 = [logit(a), ...trend ? [logit(b)] : [], ...seasonal ? [logit(g)] : [], 0, ...trend ? [0] : []];
|
|
6942
|
+
let found = nelderMead(objective, x0, 1, 300 * x0.length);
|
|
6943
|
+
found = nelderMead(objective, found.x, 0.25, 200 * x0.length);
|
|
6944
|
+
if (best === null || found.fx < best.fx) best = found;
|
|
6945
|
+
}
|
|
6946
|
+
if (best === null || !Number.isFinite(best.fx)) return null;
|
|
6947
|
+
const params = decode(best.x);
|
|
6948
|
+
const state = run(y, spec, params);
|
|
6949
|
+
if (!state) return null;
|
|
6950
|
+
const k = 2 + (trend ? 2 : 0) + (seasonal ? 1 + (m - 1) : 0);
|
|
6951
|
+
const sse = Math.max(state.sse, 1e-12 * Math.max(1, mean * mean) * n);
|
|
6952
|
+
const aic = n * Math.log(sse / n) + 2 * k;
|
|
6953
|
+
const aicc = n - k - 1 > 0 ? aic + 2 * k * (k + 1) / (n - k - 1) : Infinity;
|
|
6954
|
+
return { spec, params, state, k, aicc };
|
|
6955
|
+
}
|
|
6956
|
+
function varianceMultiplier(fit, h, seasonIndex) {
|
|
6957
|
+
const { alpha: a, beta: b, gamma: g } = fit.params;
|
|
6958
|
+
const { m } = fit.spec;
|
|
6959
|
+
const trend = fit.spec.trend === "additive";
|
|
6960
|
+
const seasonal = fit.spec.season !== "none";
|
|
6961
|
+
const k = Math.floor((h - 1) / m);
|
|
6962
|
+
let extra = trend ? (h - 1) * (a * a + a * b * h + b * b * h * (2 * h - 1) / 6) : (h - 1) * a * a;
|
|
6963
|
+
if (seasonal) extra += trend ? g * k * (2 * a + g + b * m * (k + 1)) : g * k * (2 * a + g);
|
|
6964
|
+
if (fit.spec.season === "multiplicative") extra *= seasonIndex * seasonIndex;
|
|
6965
|
+
return 1 + extra;
|
|
6966
|
+
}
|
|
6967
|
+
function forecast(values, options = {}) {
|
|
6968
|
+
const finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
6969
|
+
let first = 0;
|
|
6970
|
+
while (first < values.length && !finite(values[first])) first++;
|
|
6971
|
+
let last = values.length - 1;
|
|
6972
|
+
while (last >= first && !finite(values[last])) last--;
|
|
6973
|
+
if (last < first) return null;
|
|
6974
|
+
const ignore = Math.max(0, Math.floor(options.ignoreLast ?? 0));
|
|
6975
|
+
const end = last + 1 - ignore;
|
|
6976
|
+
let start = first;
|
|
6977
|
+
if (end - start > FORECAST_MAX_FIT_POINTS) start = end - FORECAST_MAX_FIT_POINTS;
|
|
6978
|
+
if (end - start < FORECAST_MIN_POINTS) return null;
|
|
6979
|
+
const y = [];
|
|
6980
|
+
for (let t = start; t < end; t++) {
|
|
6981
|
+
const v = values[t];
|
|
6982
|
+
if (finite(v)) {
|
|
6983
|
+
y.push(v);
|
|
6984
|
+
continue;
|
|
6985
|
+
}
|
|
6986
|
+
let lo = t - 1;
|
|
6987
|
+
while (lo >= start && !finite(values[lo])) lo--;
|
|
6988
|
+
let hi = t + 1;
|
|
6989
|
+
while (hi < end && !finite(values[hi])) hi++;
|
|
6990
|
+
const a = lo >= start ? values[lo] : void 0;
|
|
6991
|
+
const b = hi < end ? values[hi] : void 0;
|
|
6992
|
+
y.push(a !== void 0 && b !== void 0 ? a + (b - a) * (t - lo) / (hi - lo) : a ?? b ?? 0);
|
|
6993
|
+
}
|
|
6994
|
+
const n = y.length;
|
|
6995
|
+
const m = Math.max(1, Math.floor(options.seasonLength ?? 1));
|
|
6996
|
+
const seasonOption = options.season ?? "auto";
|
|
6997
|
+
const trendOption = options.trend ?? "auto";
|
|
6998
|
+
const canSeason = m > 1 && n >= 2 * m && seasonOption !== "none";
|
|
6999
|
+
const positive = y.every((v) => v > 0);
|
|
7000
|
+
const seasons = !canSeason ? ["none"] : seasonOption === "additive" ? ["additive"] : seasonOption === "multiplicative" ? [positive ? "multiplicative" : "additive"] : positive ? ["none", "additive", "multiplicative"] : ["none", "additive"];
|
|
7001
|
+
const trends = trendOption === "none" ? ["none"] : trendOption === "auto" ? ["none", "additive"] : ["additive"];
|
|
7002
|
+
let chosen = null;
|
|
7003
|
+
for (const trend of trends) {
|
|
7004
|
+
for (const season of seasons) {
|
|
7005
|
+
const fit = fitModel(y, { trend, season, m: season === "none" ? 1 : m });
|
|
7006
|
+
if (!fit) continue;
|
|
7007
|
+
if (chosen === null || fit.aicc < chosen.aicc || !Number.isFinite(chosen.aicc) && fit.k < chosen.k) chosen = fit;
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
if (!chosen) return null;
|
|
7011
|
+
const dof = n - (chosen.k - 1);
|
|
7012
|
+
const sigma = Math.sqrt(chosen.state.sse / (dof >= 1 ? dof : n));
|
|
7013
|
+
const confidence = options.confidence !== void 0 && options.confidence > 0 && options.confidence < 1 ? options.confidence : 0.95;
|
|
7014
|
+
const z = inverseNormal(1 - (1 - confidence) / 2);
|
|
7015
|
+
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)));
|
|
7016
|
+
const steps = values.length - end + horizon;
|
|
7017
|
+
const { spec, state } = chosen;
|
|
7018
|
+
const points = [];
|
|
7019
|
+
for (let h = 1; h <= steps; h++) {
|
|
7020
|
+
const base = state.level + h * state.slope;
|
|
7021
|
+
const s = spec.season === "none" ? void 0 : state.season[(n - 1 + h) % spec.m];
|
|
7022
|
+
const value = s === void 0 ? base : spec.season === "multiplicative" ? base * s : base + s;
|
|
7023
|
+
const half = z * sigma * Math.sqrt(varianceMultiplier(chosen, h, s ?? 1));
|
|
7024
|
+
points.push({ value, lower: value - half, upper: value + half });
|
|
7025
|
+
}
|
|
7026
|
+
const { alpha, beta, gamma } = chosen.params;
|
|
7027
|
+
return {
|
|
7028
|
+
model: {
|
|
7029
|
+
trend: spec.trend,
|
|
7030
|
+
season: spec.season,
|
|
7031
|
+
alpha,
|
|
7032
|
+
...spec.trend === "additive" ? { beta } : {},
|
|
7033
|
+
...spec.season !== "none" ? { gamma, seasonLength: spec.m } : {}
|
|
7034
|
+
},
|
|
7035
|
+
fitted: n,
|
|
7036
|
+
end,
|
|
7037
|
+
sigma,
|
|
7038
|
+
points
|
|
7039
|
+
};
|
|
7040
|
+
}
|
|
7041
|
+
var TEXT19 = {
|
|
7042
|
+
ja: {
|
|
7043
|
+
forecast: "\u4E88\u6E2C",
|
|
7044
|
+
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",
|
|
7045
|
+
interval: (pct, lo, hi) => `${pct} \u4E88\u6E2C\u533A\u9593 ${lo}\u301C${hi}`,
|
|
7046
|
+
chart: (names) => `${names.join("\u30FB")}\u306E\u5B9F\u7E3E\u3068\u4E88\u6E2C\u306E\u6298\u308C\u7DDA\u30B0\u30E9\u30D5`,
|
|
7047
|
+
item: "\u671F\u9593"
|
|
7048
|
+
},
|
|
7049
|
+
en: {
|
|
7050
|
+
forecast: "Forecast",
|
|
7051
|
+
note: (source) => `Forecast is an approximation (exponential smoothing); values do not match ${source ? `${source}'s` : "the source tool's"} forecast.`,
|
|
7052
|
+
interval: (pct, lo, hi) => `${pct} interval ${lo} \u2013 ${hi}`,
|
|
7053
|
+
chart: (names) => `Line chart of actuals and forecast for ${names.join(", ")}`,
|
|
7054
|
+
item: "Period"
|
|
7055
|
+
}
|
|
7056
|
+
};
|
|
7057
|
+
var LEGEND_DASHED_ICON = "path://M0,0 L42,0 L42,24 L0,24 Z M58,0 L100,0 L100,24 L58,24 Z";
|
|
7058
|
+
var BOUNDARY_LABEL_ROOM = 18;
|
|
7059
|
+
var MAX_PERIODS = 5e3;
|
|
7060
|
+
var DAY_MS = 864e5;
|
|
7061
|
+
var NO_ROWS14 = [];
|
|
7062
|
+
var DATE_RE = /^(\d{4})(?:[-/](\d{1,2})(?:[-/](\d{1,2}))?)?(?:[T ].*)?$/;
|
|
7063
|
+
var QUARTER_RE = /^(\d{4})[-\s]?Q([1-4])$/i;
|
|
7064
|
+
function parseDate(value) {
|
|
7065
|
+
if (value instanceof Date) {
|
|
7066
|
+
return Number.isNaN(value.getTime()) ? null : { y: value.getFullYear(), m: value.getMonth() + 1, d: value.getDate(), precision: "day" };
|
|
7067
|
+
}
|
|
7068
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
7069
|
+
if (Number.isInteger(value) && value >= 1e3 && value <= 9999) return { y: value, m: 1, d: 1, precision: "year" };
|
|
7070
|
+
return parseDate(new Date(value));
|
|
7071
|
+
}
|
|
7072
|
+
if (typeof value !== "string") return null;
|
|
7073
|
+
const text = value.trim();
|
|
7074
|
+
const q = QUARTER_RE.exec(text);
|
|
7075
|
+
if (q) return { y: Number(q[1]), m: (Number(q[2]) - 1) * 3 + 1, d: 1, precision: "month" };
|
|
7076
|
+
const m = DATE_RE.exec(text);
|
|
7077
|
+
if (m) {
|
|
7078
|
+
const month = m[2] === void 0 ? 1 : Number(m[2]);
|
|
7079
|
+
const day = m[3] === void 0 ? 1 : Number(m[3]);
|
|
7080
|
+
if (month < 1 || month > 12 || day < 1 || day > 31) return null;
|
|
7081
|
+
return { y: Number(m[1]), m: month, d: day, precision: m[3] !== void 0 ? "day" : m[2] !== void 0 ? "month" : "year" };
|
|
7082
|
+
}
|
|
7083
|
+
if (text === "") return null;
|
|
7084
|
+
const parsed = new Date(text);
|
|
7085
|
+
return Number.isNaN(parsed.getTime()) ? null : parseDate(parsed);
|
|
7086
|
+
}
|
|
7087
|
+
var dayOrdinal = (p) => Math.round(Date.UTC(p.y, p.m - 1, p.d) / DAY_MS);
|
|
7088
|
+
function ordinal(p, period) {
|
|
7089
|
+
switch (period) {
|
|
7090
|
+
case "year":
|
|
7091
|
+
return p.y;
|
|
7092
|
+
case "quarter":
|
|
7093
|
+
return p.y * 4 + Math.floor((p.m - 1) / 3);
|
|
7094
|
+
case "month":
|
|
7095
|
+
return p.y * 12 + (p.m - 1);
|
|
7096
|
+
default:
|
|
7097
|
+
return dayOrdinal(p);
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
var pad2 = (n) => String(n).padStart(2, "0");
|
|
7101
|
+
function ordinalLabel(o, period) {
|
|
7102
|
+
if (period === "year") return String(o);
|
|
7103
|
+
if (period === "quarter") return `${Math.floor(o / 4)}-${pad2(o % 4 * 3 + 1)}`;
|
|
7104
|
+
if (period === "month") return `${Math.floor(o / 12)}-${pad2(o % 12 + 1)}`;
|
|
7105
|
+
const d = new Date(o * DAY_MS);
|
|
7106
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
|
|
7107
|
+
}
|
|
7108
|
+
function inferPeriod(dates) {
|
|
7109
|
+
if (dates.every((d) => d.precision === "year")) return "year";
|
|
7110
|
+
const coarse = dates.every((d) => d.precision !== "day");
|
|
7111
|
+
const keys = [...new Set(dates.map((d) => coarse ? ordinal(d, "month") : dayOrdinal(d)))].sort((a, b) => a - b);
|
|
7112
|
+
const gaps = keys.slice(1).map((k, i) => k - keys[i]).sort((a, b) => a - b);
|
|
7113
|
+
const gap = gaps.length === 0 ? coarse ? 1 : 1 : gaps[Math.floor(gaps.length / 2)];
|
|
7114
|
+
if (coarse) return gap >= 12 ? "year" : gap >= 3 ? "quarter" : "month";
|
|
7115
|
+
return gap >= 360 ? "year" : gap >= 85 ? "quarter" : gap >= 28 ? "month" : gap >= 7 ? "week" : "day";
|
|
7116
|
+
}
|
|
7117
|
+
var SEASON_LENGTH = { day: 7, week: 52, month: 12, quarter: 4, year: 1 };
|
|
7118
|
+
function buildForecastModel(data, x, y, seriesKey, period, options, singleName, otherLabel) {
|
|
7119
|
+
if (!x || !y || !Array.isArray(data)) return null;
|
|
7120
|
+
const points = [];
|
|
7121
|
+
for (const row of data) {
|
|
7122
|
+
if (!row || typeof row !== "object") continue;
|
|
7123
|
+
const date = parseDate(row[x]);
|
|
7124
|
+
const value = toChartNumber(row[y]);
|
|
7125
|
+
if (!date || value === null) continue;
|
|
7126
|
+
const raw = seriesKey ? row[seriesKey] : singleName;
|
|
7127
|
+
const name = raw === null || raw === void 0 || raw === "" ? "\u2014" : String(raw);
|
|
7128
|
+
points.push({ date, name, value });
|
|
7129
|
+
}
|
|
7130
|
+
if (points.length === 0) return null;
|
|
7131
|
+
const grain = period ?? inferPeriod(points.map((p) => p.date));
|
|
7132
|
+
const step = grain === "week" ? 7 : 1;
|
|
7133
|
+
const keyed = points.map((p) => ({ ...p, key: ordinal(p.date, grain) }));
|
|
7134
|
+
let min = Math.min(...keyed.map((p) => p.key));
|
|
7135
|
+
const max = Math.max(...keyed.map((p) => p.key));
|
|
7136
|
+
if ((max - min) / step + 1 > MAX_PERIODS) min = max - (MAX_PERIODS - 1) * step;
|
|
7137
|
+
const count = Math.floor((max - min) / step) + 1;
|
|
7138
|
+
const slot = (key) => Math.floor((key - min) / step);
|
|
7139
|
+
const sums = /* @__PURE__ */ new Map();
|
|
7140
|
+
for (const p of keyed) {
|
|
7141
|
+
const at = slot(p.key);
|
|
7142
|
+
if (at < 0) continue;
|
|
7143
|
+
let list = sums.get(p.name);
|
|
7144
|
+
if (!list) {
|
|
7145
|
+
list = Array(count).fill(null);
|
|
7146
|
+
sums.set(p.name, list);
|
|
7147
|
+
}
|
|
7148
|
+
list[at] = (list[at] ?? 0) + p.value;
|
|
7149
|
+
}
|
|
7150
|
+
const all = [...sums.keys()];
|
|
7151
|
+
const totals = all.map((name) => ({ name, value: sums.get(name).reduce((a, v) => a + (v ?? 0), 0) }));
|
|
7152
|
+
const { top, rest, other } = aggregateTopN(totals, void 0, otherLabel);
|
|
7153
|
+
const names = top.map((t) => t.name);
|
|
7154
|
+
const actual = names.map((n) => sums.get(n));
|
|
7155
|
+
if (other) {
|
|
7156
|
+
const folded = Array(count).fill(null);
|
|
7157
|
+
for (const r of rest) sums.get(r.name).forEach((v, i) => v === null ? void 0 : folded[i] = (folded[i] ?? 0) + v);
|
|
7158
|
+
names.push(other.name);
|
|
7159
|
+
actual.push(folded);
|
|
7160
|
+
}
|
|
7161
|
+
const resolved = { ...options, seasonLength: options?.seasonLength ?? SEASON_LENGTH[grain] };
|
|
7162
|
+
const results = actual.map((values) => {
|
|
7163
|
+
try {
|
|
7164
|
+
return forecast(values.map((v) => v === null ? NaN : v), resolved);
|
|
7165
|
+
} catch {
|
|
7166
|
+
return null;
|
|
7167
|
+
}
|
|
7168
|
+
});
|
|
7169
|
+
const length = Math.max(count, ...results.map((r) => r ? r.end + r.points.length : 0));
|
|
7170
|
+
const labels = Array.from({ length }, (_, i) => ordinalLabel(min + i * step, grain));
|
|
7171
|
+
const predicted = results.map((r) => {
|
|
7172
|
+
const out = Array(length).fill(null);
|
|
7173
|
+
r?.points.forEach((p, k) => out[r.end + k] = p);
|
|
7174
|
+
return out;
|
|
7175
|
+
});
|
|
7176
|
+
return {
|
|
7177
|
+
labels,
|
|
7178
|
+
actualCount: count,
|
|
7179
|
+
names,
|
|
7180
|
+
otherName: other ? other.name : null,
|
|
7181
|
+
actual: actual.map((list) => [...list, ...Array(length - count).fill(null)]),
|
|
7182
|
+
predicted,
|
|
7183
|
+
fittedEnd: results.map((r) => r ? r.end - 1 : -1),
|
|
7184
|
+
results
|
|
7185
|
+
};
|
|
7186
|
+
}
|
|
7187
|
+
var FORECAST_ID = "__forecast:";
|
|
7188
|
+
var BAND_BASE_ID = "__forecast-band-base:";
|
|
7189
|
+
var BAND_RANGE_ID = "__forecast-band-range:";
|
|
7190
|
+
function ForecastChartImpl({
|
|
7191
|
+
data = NO_ROWS14,
|
|
7192
|
+
x,
|
|
7193
|
+
y,
|
|
7194
|
+
series: seriesKey,
|
|
7195
|
+
period,
|
|
7196
|
+
forecast: forecast2,
|
|
7197
|
+
forecastLabel,
|
|
7198
|
+
forecastSource,
|
|
7199
|
+
forecastNote,
|
|
7200
|
+
colors,
|
|
7201
|
+
labels,
|
|
7202
|
+
valueFormatter,
|
|
7203
|
+
format,
|
|
7204
|
+
title,
|
|
7205
|
+
summary,
|
|
7206
|
+
actions,
|
|
7207
|
+
fileName,
|
|
7208
|
+
isLoading,
|
|
7209
|
+
emptyState,
|
|
7210
|
+
height,
|
|
7211
|
+
chartOptions,
|
|
7212
|
+
showLegend = true,
|
|
7213
|
+
showTooltip = true,
|
|
7214
|
+
showGridLines = true,
|
|
7215
|
+
showXAxis = true,
|
|
7216
|
+
showYAxis = true,
|
|
7217
|
+
yAxisWidth,
|
|
7218
|
+
minValue,
|
|
7219
|
+
maxValue,
|
|
7220
|
+
fitToData = false,
|
|
7221
|
+
xAxisLabel,
|
|
7222
|
+
yAxisLabel,
|
|
7223
|
+
ariaLabel,
|
|
7224
|
+
className,
|
|
7225
|
+
...rest
|
|
7226
|
+
}, ref) {
|
|
7227
|
+
const { ref: frameRef, surface, width: plotWidth, tokens } = useChartSurface(ref);
|
|
7228
|
+
const locale = useLocale();
|
|
7229
|
+
const t = TEXT19[uiLanguage(locale)];
|
|
7230
|
+
const fcLabel = forecastLabel ?? t.forecast;
|
|
7231
|
+
const tooltipFormatter = useMemo(() => resolveValueFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7232
|
+
const axisFormatter = useMemo(() => resolveAxisFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7233
|
+
const axisTicks = useMemo(() => resolveAxisTickFormatter(valueFormatter, format, locale), [valueFormatter, format, locale]);
|
|
7234
|
+
const singleName = labels?.[y] ?? y ?? "";
|
|
7235
|
+
const model = useMemo(
|
|
7236
|
+
() => buildForecastModel(data, x, y, seriesKey, period, forecast2, singleName, otherSeriesLabel(locale)),
|
|
7237
|
+
// forecast はオブジェクトリテラルで渡されることが多いので中身で比べる
|
|
7238
|
+
[data, x, y, seriesKey, period, JSON.stringify(forecast2 ?? null), singleName, locale]
|
|
7239
|
+
);
|
|
7240
|
+
const palette = useMemo(
|
|
7241
|
+
() => model ? resolveChartColors(seriesColorsFor(model.names, colors, CHART_OTHER_COLOR, model.otherName), tokens) : [],
|
|
7242
|
+
[model, colors, tokens]
|
|
7243
|
+
);
|
|
7244
|
+
const option = useMemo(() => {
|
|
7245
|
+
if (!model) return null;
|
|
7246
|
+
const index = "__period";
|
|
7247
|
+
const rows = model.labels.map((label3, i) => {
|
|
7248
|
+
const row = { [index]: label3 };
|
|
7249
|
+
model.names.forEach((name, c) => row[name] = model.actual[c][i]);
|
|
7250
|
+
return row;
|
|
7251
|
+
});
|
|
7252
|
+
const base = buildCartesianOption({
|
|
7253
|
+
kind: "line",
|
|
7254
|
+
data: rows,
|
|
7255
|
+
index,
|
|
7256
|
+
categories: model.names,
|
|
7257
|
+
palette,
|
|
7258
|
+
valueFormatter: tooltipFormatter,
|
|
7259
|
+
axisFormatter,
|
|
7260
|
+
axisTicks,
|
|
7261
|
+
curveType: "linear",
|
|
7262
|
+
connectNulls: false,
|
|
7263
|
+
showLegend,
|
|
7264
|
+
showTooltip,
|
|
7265
|
+
showGridLines,
|
|
7266
|
+
showXAxis,
|
|
7267
|
+
showYAxis,
|
|
7268
|
+
startEndOnly: false,
|
|
7269
|
+
yAxisWidth,
|
|
7270
|
+
minValue,
|
|
7271
|
+
maxValue,
|
|
7272
|
+
fitToData,
|
|
7273
|
+
allowDecimals: true,
|
|
7274
|
+
xAxisLabel,
|
|
7275
|
+
yAxisLabel,
|
|
7276
|
+
showDots: false,
|
|
7277
|
+
active: null,
|
|
7278
|
+
highlight: "none",
|
|
7279
|
+
reserveActions: Boolean(actions),
|
|
7280
|
+
guideColor: tokens?.mutedForeground ?? void 0,
|
|
7281
|
+
labelBackground: surface,
|
|
7282
|
+
labelColor: tokens?.foreground ?? void 0,
|
|
7283
|
+
plotWidth,
|
|
7284
|
+
plotHeight: typeof height === "number" ? height : void 0,
|
|
7285
|
+
locale
|
|
7286
|
+
});
|
|
7287
|
+
const bandAlpha = isDarkSurface(surface) ? FORECAST_BAND_ALPHA_DARK : FORECAST_BAND_ALPHA_LIGHT;
|
|
7288
|
+
const helper = {
|
|
7289
|
+
type: "line",
|
|
7290
|
+
symbol: "none",
|
|
7291
|
+
showSymbol: false,
|
|
7292
|
+
connectNulls: true,
|
|
7293
|
+
legendHoverLink: false,
|
|
7294
|
+
emphasis: { disabled: true }
|
|
7295
|
+
};
|
|
7296
|
+
const lines = [];
|
|
7297
|
+
const bands = [];
|
|
7298
|
+
model.names.forEach((name, c) => {
|
|
7299
|
+
const start = model.fittedEnd[c];
|
|
7300
|
+
if (start < 0) return;
|
|
7301
|
+
const color = palette[c] || void 0;
|
|
7302
|
+
const predicted = model.predicted[c];
|
|
7303
|
+
const startValue = model.actual[c][start] ?? null;
|
|
7304
|
+
const line = predicted.map((p, i) => i === start ? startValue : p?.value ?? null);
|
|
7305
|
+
const lower = predicted.map((p, i) => i === start ? startValue : p ? Math.min(p.lower, p.upper) : null);
|
|
7306
|
+
const range = predicted.map((p, i) => i === start ? startValue === null ? null : 0 : p ? Math.abs(p.upper - p.lower) : null);
|
|
7307
|
+
lines.push({
|
|
7308
|
+
...helper,
|
|
7309
|
+
id: FORECAST_ID + name,
|
|
7310
|
+
name: fcLabel,
|
|
7311
|
+
z: 3,
|
|
7312
|
+
lineStyle: { width: 2, type: FORECAST_LINE_DASH, color, opacity: 0.7 },
|
|
7313
|
+
itemStyle: { color, opacity: 0.7 },
|
|
7314
|
+
data: line
|
|
7315
|
+
});
|
|
7316
|
+
bands.push(
|
|
7317
|
+
{ ...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 },
|
|
7318
|
+
{
|
|
7319
|
+
...helper,
|
|
7320
|
+
id: BAND_RANGE_ID + name,
|
|
7321
|
+
name: fcLabel,
|
|
7322
|
+
silent: true,
|
|
7323
|
+
z: 1,
|
|
7324
|
+
stack: BAND_BASE_ID + name,
|
|
7325
|
+
tooltip: { show: false },
|
|
7326
|
+
lineStyle: { opacity: 0 },
|
|
7327
|
+
itemStyle: { color },
|
|
7328
|
+
areaStyle: color ? { color: withAlpha(color, bandAlpha), opacity: 1 } : { opacity: bandAlpha },
|
|
7329
|
+
data: range
|
|
7330
|
+
}
|
|
7331
|
+
);
|
|
7332
|
+
});
|
|
7333
|
+
if (lines.length === 0) return mergeChartOptions(base, chartOptions);
|
|
7334
|
+
const boundary = Math.max(...model.fittedEnd);
|
|
7335
|
+
const total = model.labels.length;
|
|
7336
|
+
const span = plotWidth && total > 1 ? (plotWidth - 64) / (total - 1) * (total - 1 - boundary) : null;
|
|
7337
|
+
const boundaryText = forecastBoundaryText(fcLabel);
|
|
7338
|
+
lines[0] = {
|
|
7339
|
+
...lines[0],
|
|
7340
|
+
markLine: {
|
|
7341
|
+
silent: true,
|
|
7342
|
+
symbol: "none",
|
|
7343
|
+
animation: false,
|
|
7344
|
+
data: [
|
|
7345
|
+
{
|
|
7346
|
+
name: "forecast-boundary",
|
|
7347
|
+
xAxis: boundary,
|
|
7348
|
+
lineStyle: { type: "dashed", width: 1, color: tokens?.mutedForeground ?? void 0 },
|
|
7349
|
+
label: {
|
|
7350
|
+
show: true,
|
|
7351
|
+
position: "end",
|
|
7352
|
+
rotate: 0,
|
|
7353
|
+
align: forecastLabelAlign(span, boundaryText),
|
|
7354
|
+
verticalAlign: "bottom",
|
|
7355
|
+
distance: 4,
|
|
7356
|
+
fontSize: 12,
|
|
7357
|
+
color: tokens?.mutedForeground ?? void 0,
|
|
7358
|
+
formatter: boundaryText
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
]
|
|
7362
|
+
}
|
|
7363
|
+
};
|
|
7364
|
+
const legend = base.legend;
|
|
7365
|
+
const grid = base.grid;
|
|
7366
|
+
const times = timeTooltipLabels(model.labels, locale);
|
|
7367
|
+
const pct = `${Math.round((forecast2?.confidence && forecast2.confidence > 0 && forecast2.confidence < 1 ? forecast2.confidence : 0.95) * 100)}%`;
|
|
7368
|
+
const option2 = {
|
|
7369
|
+
...base,
|
|
7370
|
+
legend: legend ? { ...legend, data: [...legend.data ?? [], { name: fcLabel, icon: LEGEND_DASHED_ICON }] } : legend,
|
|
7371
|
+
grid: grid ? { ...grid, top: (grid.top ?? 12) + BOUNDARY_LABEL_ROOM } : grid,
|
|
7372
|
+
tooltip: {
|
|
7373
|
+
...base.tooltip,
|
|
7374
|
+
formatter: (params) => {
|
|
7375
|
+
const list = Array.isArray(params) ? params : [params];
|
|
7376
|
+
const i = list[0]?.dataIndex ?? 0;
|
|
7377
|
+
const shown = new Set(list.map((p) => p.seriesId ?? ""));
|
|
7378
|
+
const visible = (id) => list.length === 0 || shown.has(id);
|
|
7379
|
+
const rows2 = [];
|
|
7380
|
+
model.names.forEach((name, c) => {
|
|
7381
|
+
const color = palette[c] || "currentColor";
|
|
7382
|
+
const value = model.actual[c][i] ?? null;
|
|
7383
|
+
if (value !== null && visible(`series:${name}`)) rows2.push({ name, color, value });
|
|
7384
|
+
const p = model.predicted[c][i];
|
|
7385
|
+
if (p && visible(FORECAST_ID + name)) {
|
|
7386
|
+
rows2.push({
|
|
7387
|
+
name: model.names.length > 1 ? `${name} \xB7 ${fcLabel}` : fcLabel,
|
|
7388
|
+
color,
|
|
7389
|
+
value: p.value,
|
|
7390
|
+
note: t.interval(pct, tooltipFormatter(Math.min(p.lower, p.upper)), tooltipFormatter(Math.max(p.lower, p.upper)))
|
|
7391
|
+
});
|
|
7392
|
+
}
|
|
7393
|
+
});
|
|
7394
|
+
return tooltipHtml(times?.[i] ?? model.labels[i] ?? "", rows2, tooltipFormatter, [], locale);
|
|
7395
|
+
}
|
|
7396
|
+
},
|
|
7397
|
+
series: [...base.series ?? [], ...lines, ...bands]
|
|
7398
|
+
};
|
|
7399
|
+
return mergeChartOptions(option2, chartOptions);
|
|
7400
|
+
}, [
|
|
7401
|
+
model,
|
|
7402
|
+
palette,
|
|
7403
|
+
tooltipFormatter,
|
|
7404
|
+
axisFormatter,
|
|
7405
|
+
axisTicks,
|
|
7406
|
+
showLegend,
|
|
7407
|
+
showTooltip,
|
|
7408
|
+
showGridLines,
|
|
7409
|
+
showXAxis,
|
|
7410
|
+
showYAxis,
|
|
7411
|
+
yAxisWidth,
|
|
7412
|
+
minValue,
|
|
7413
|
+
maxValue,
|
|
7414
|
+
fitToData,
|
|
7415
|
+
xAxisLabel,
|
|
7416
|
+
yAxisLabel,
|
|
7417
|
+
actions,
|
|
7418
|
+
tokens,
|
|
7419
|
+
surface,
|
|
7420
|
+
plotWidth,
|
|
7421
|
+
height,
|
|
7422
|
+
locale,
|
|
7423
|
+
fcLabel,
|
|
7424
|
+
forecast2?.confidence,
|
|
7425
|
+
t,
|
|
7426
|
+
chartOptions
|
|
7427
|
+
]);
|
|
7428
|
+
const dataTable = useMemo(() => {
|
|
7429
|
+
if (!model) return void 0;
|
|
7430
|
+
const forecastColumns = model.names.map((name) => `${name} (${fcLabel})`);
|
|
7431
|
+
const rows = model.labels.map((label3, i) => {
|
|
7432
|
+
const row = { period: label3 };
|
|
7433
|
+
model.names.forEach((name, c) => {
|
|
7434
|
+
row[name] = model.actual[c][i];
|
|
7435
|
+
row[forecastColumns[c]] = model.predicted[c][i]?.value ?? null;
|
|
7436
|
+
});
|
|
7437
|
+
return row;
|
|
7438
|
+
});
|
|
7439
|
+
const columns = model.names.flatMap((name, c) => model.fittedEnd[c] >= 0 ? [name, forecastColumns[c]] : [name]);
|
|
7440
|
+
const table = chartDataTable(rows, "period", columns, tooltipFormatter, void 0, t.item);
|
|
7441
|
+
const times = timeTooltipLabels(model.labels, locale);
|
|
7442
|
+
return times ? { ...table, rows: table.rows.map((row, i) => [times[i] ?? row[0] ?? "", ...row.slice(1)]) } : table;
|
|
7443
|
+
}, [model, fcLabel, tooltipFormatter, t, locale]);
|
|
7444
|
+
const label2 = ariaLabel ?? t.chart(model?.names ?? [singleName]);
|
|
7445
|
+
return /* @__PURE__ */ jsx(
|
|
7446
|
+
ChartFrame,
|
|
7447
|
+
{
|
|
7448
|
+
...chartRootProps(rest),
|
|
7449
|
+
ref: frameRef,
|
|
7450
|
+
title,
|
|
7451
|
+
summary,
|
|
7452
|
+
isLoading: isLoading ?? false,
|
|
7453
|
+
isEmpty: !model || !option,
|
|
7454
|
+
emptyState,
|
|
7455
|
+
height,
|
|
7456
|
+
variant: "line",
|
|
7457
|
+
ariaLabel: label2,
|
|
7458
|
+
className,
|
|
7459
|
+
dataTable,
|
|
7460
|
+
footnote: [t.note(forecastSource), forecastNote].filter(Boolean).join(" "),
|
|
7461
|
+
children: option && /* @__PURE__ */ jsx(EChart, { option, height: "100%", ariaLabel: label2, actions, fileName, exportHeader: { title, summary } })
|
|
7462
|
+
}
|
|
7463
|
+
);
|
|
7464
|
+
}
|
|
7465
|
+
var ForecastChart = forwardRef(ForecastChartImpl);
|
|
7466
|
+
ForecastChart.displayName = "ForecastChart";
|
|
7467
|
+
|
|
7468
|
+
// ../squadkit/registry/lib/color-ramp.ts
|
|
7469
|
+
var RAMP_FLOOR_SHARE = 0.2;
|
|
7470
|
+
var RAMP_LIGHT_TEXT_FROM = 0.55;
|
|
7471
|
+
function parseRgb2(color) {
|
|
7472
|
+
const hex = /^#([0-9a-f]{6})$/i.exec(color.trim());
|
|
7473
|
+
if (hex) {
|
|
7474
|
+
const n = parseInt(hex[1], 16);
|
|
7475
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
7476
|
+
}
|
|
7477
|
+
const rgb = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(color.trim());
|
|
7478
|
+
return rgb ? [Number(rgb[1]), Number(rgb[2]), Number(rgb[3])] : null;
|
|
7479
|
+
}
|
|
7480
|
+
var toLinear2 = (c) => (c /= 255) <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
7481
|
+
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)));
|
|
7482
|
+
function toOklab2([r, g, b]) {
|
|
7483
|
+
const [lr, lg, lb] = [toLinear2(r), toLinear2(g), toLinear2(b)];
|
|
7484
|
+
const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
|
|
7485
|
+
const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
|
|
7486
|
+
const t = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
|
|
7487
|
+
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];
|
|
7488
|
+
}
|
|
7489
|
+
function fromOklab2([L, a, b]) {
|
|
7490
|
+
const l = (L + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
7491
|
+
const m = (L - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
7492
|
+
const t = (L - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
7493
|
+
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];
|
|
7494
|
+
return `rgb(${rgb.map(fromLinear2).join(", ")})`;
|
|
7495
|
+
}
|
|
7496
|
+
function withAlpha3(color, alpha) {
|
|
7497
|
+
const c = parseRgb2(color);
|
|
7498
|
+
return c ? `rgba(${c[0]}, ${c[1]}, ${c[2]}, ${alpha})` : color;
|
|
7499
|
+
}
|
|
7500
|
+
function mixIntoSurface2(color, surface, share) {
|
|
7501
|
+
const c = parseRgb2(color);
|
|
7502
|
+
const s = surface ? parseRgb2(surface) : null;
|
|
7503
|
+
if (!c || !s) return withAlpha3(color, share);
|
|
7504
|
+
const [c1, c2] = [toOklab2(c), toOklab2(s)];
|
|
7505
|
+
return fromOklab2([0, 1, 2].map((i) => c2[i] + (c1[i] - c2[i]) * share));
|
|
7506
|
+
}
|
|
7507
|
+
function rampRatio(value, min, max) {
|
|
7508
|
+
return max > min ? Math.min(1, Math.max(0, (value - min) / (max - min))) : 1;
|
|
7509
|
+
}
|
|
7510
|
+
function rampColor(color, surface, ratio) {
|
|
7511
|
+
return mixIntoSurface2(color, surface, RAMP_FLOOR_SHARE + (1 - RAMP_FLOOR_SHARE) * Math.min(1, Math.max(0, ratio)));
|
|
7512
|
+
}
|
|
7513
|
+
function rampGradientCss(tone) {
|
|
7514
|
+
return `linear-gradient(to right, color-mix(in oklch, var(--${tone}) ${RAMP_FLOOR_SHARE * 100}%, var(--card)), var(--${tone}))`;
|
|
7515
|
+
}
|
|
7516
|
+
var GEO_MAPS = ["japan", "us", "world"];
|
|
7517
|
+
var geoMapKey = (name) => `squadkit-${name}`;
|
|
7518
|
+
var GEO_ASPECT_SCALE = { japan: 0.8, us: 0.8, world: 0.75 };
|
|
7519
|
+
function fold(text) {
|
|
7520
|
+
return text.normalize("NFKC").normalize("NFD").replace(/[̀-ͯ]/g, "").normalize("NFC").toLowerCase().trim();
|
|
7521
|
+
}
|
|
7522
|
+
var SEPARATORS = /[\s.,'’`"()_\-‐・・]/g;
|
|
7523
|
+
var squash = (text) => text.replace(SEPARATORS, "");
|
|
7524
|
+
function regionKeys(raw) {
|
|
7525
|
+
if (raw === null || raw === void 0) return [];
|
|
7526
|
+
const text = fold(String(raw));
|
|
7527
|
+
if (!text) return [];
|
|
7528
|
+
const keys = [squash(text)];
|
|
7529
|
+
const stripped = text.replace(/[\s-]+(prefecture|pref|ken|fu|to|state)$/, "").replace(/^(state of|the)\s+/, "").replace(/(?<=..)(都|府|県|州)$/, "");
|
|
7530
|
+
if (stripped !== text) keys.push(squash(stripped));
|
|
7531
|
+
if (/^\d{1,2}$/.test(text)) keys.push(`jp${text.padStart(2, "0")}`);
|
|
7532
|
+
return [...new Set(keys.filter(Boolean))];
|
|
7533
|
+
}
|
|
7534
|
+
var ALIASES = {
|
|
7535
|
+
"US-DC": ["District of Columbia", "Washington DC", "Washington, D.C.", "\u30EF\u30B7\u30F3\u30C8\u30F3DC", "\u30B3\u30ED\u30F3\u30D3\u30A2\u7279\u5225\u533A"],
|
|
7536
|
+
USA: ["United States", "US", "USA", "America", "\u7C73\u56FD", "\u30A2\u30E1\u30EA\u30AB"],
|
|
7537
|
+
GBR: ["UK", "Great Britain", "Britain", "\u82F1\u56FD"],
|
|
7538
|
+
KOR: ["Korea", "Republic of Korea", "Korea, Republic of", "\u97D3\u56FD"],
|
|
7539
|
+
PRK: ["DPRK", "\u5317\u671D\u9BAE"],
|
|
7540
|
+
CHN: ["China", "\u4E2D\u56FD"],
|
|
7541
|
+
TWN: ["TW", "\u53F0\u6E7E"],
|
|
7542
|
+
RUS: ["Russian Federation"],
|
|
7543
|
+
CZE: ["Czechia"],
|
|
7544
|
+
VNM: ["Viet Nam"],
|
|
7545
|
+
CIV: ["C\xF4te d'Ivoire"],
|
|
7546
|
+
SWZ: ["Swaziland"],
|
|
7547
|
+
MKD: ["Macedonia"],
|
|
7548
|
+
COD: ["DR Congo", "Congo-Kinshasa"],
|
|
7549
|
+
COG: ["Congo", "Congo-Brazzaville"]
|
|
7550
|
+
};
|
|
7551
|
+
var SKIP_NAME = /* @__PURE__ */ new Set(["US-DC"]);
|
|
7552
|
+
var indexCache = /* @__PURE__ */ new WeakMap();
|
|
7553
|
+
function buildGeoIndex(map, collection) {
|
|
7554
|
+
const cached = indexCache.get(collection);
|
|
7555
|
+
if (cached) return cached;
|
|
7556
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7557
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
7558
|
+
const add = (key, code) => {
|
|
7559
|
+
for (const k of regionKeys(key)) if (!byKey.has(k)) byKey.set(k, code);
|
|
7560
|
+
};
|
|
7561
|
+
for (const { properties: p } of collection.features) {
|
|
7562
|
+
byCode.set(p.code, p);
|
|
7563
|
+
add(p.code, p.code);
|
|
7564
|
+
const short = p.code.split("-")[1];
|
|
7565
|
+
if (short) add(short, p.code);
|
|
7566
|
+
if (p.alpha2) add(p.alpha2, p.code);
|
|
7567
|
+
if (map === "japan" && short) add(String(Number(short)), p.code);
|
|
7568
|
+
}
|
|
7569
|
+
for (const { properties: p } of collection.features) {
|
|
7570
|
+
if (!SKIP_NAME.has(p.code)) add(p.name, p.code);
|
|
7571
|
+
add(p.name_ja, p.code);
|
|
7572
|
+
for (const alias of ALIASES[p.code] ?? []) add(alias, p.code);
|
|
7573
|
+
}
|
|
7574
|
+
const index = { map, byKey, byCode };
|
|
7575
|
+
indexCache.set(collection, index);
|
|
7576
|
+
return index;
|
|
7577
|
+
}
|
|
7578
|
+
function matchRegion(index, raw) {
|
|
7579
|
+
for (const key of regionKeys(raw)) {
|
|
7580
|
+
const code = index.byKey.get(key);
|
|
7581
|
+
if (code) return code;
|
|
7582
|
+
}
|
|
7583
|
+
return null;
|
|
7584
|
+
}
|
|
7585
|
+
function regionName(properties, language) {
|
|
7586
|
+
return language === "ja" && properties.name_ja ? properties.name_ja : properties.name.replace(/ Prefecture$/, "");
|
|
7587
|
+
}
|
|
7588
|
+
function detectGeoMap(values, loaded2) {
|
|
7589
|
+
const distinct = [...new Set(values.filter((v) => v !== null && v !== void 0 && String(v).trim() !== "").map(String))];
|
|
7590
|
+
let best = null;
|
|
7591
|
+
let bestCount = 0;
|
|
7592
|
+
for (const map of GEO_MAPS) {
|
|
7593
|
+
const collection = loaded2[map];
|
|
7594
|
+
if (!collection) continue;
|
|
7595
|
+
const index = buildGeoIndex(map, collection);
|
|
7596
|
+
const count = distinct.filter((v) => matchRegion(index, v) !== null).length;
|
|
7597
|
+
if (count > bestCount) [best, bestCount] = [map, count];
|
|
7598
|
+
}
|
|
7599
|
+
return best;
|
|
7600
|
+
}
|
|
7601
|
+
var BOUNDS = {
|
|
7602
|
+
japan: [122, 20, 154, 46.5],
|
|
7603
|
+
us: [-180, 18, -65, 72]
|
|
7604
|
+
};
|
|
7605
|
+
function detectBasemap(points) {
|
|
7606
|
+
if (points.length === 0) return "world";
|
|
7607
|
+
for (const map of ["japan", "us"]) {
|
|
7608
|
+
const [w, s, e, n] = BOUNDS[map];
|
|
7609
|
+
if (points.every(([lon, lat]) => lon >= w && lon <= e && lat >= s && lat <= n)) return map;
|
|
7610
|
+
}
|
|
7611
|
+
return "world";
|
|
7612
|
+
}
|
|
7613
|
+
var US_INSETS = [
|
|
7614
|
+
// Alaska (the Aleutians stop short of the antimeridian in this data).
|
|
7615
|
+
{ box: [-180, 51, -129.5, 72], center: [-149, 63], to: [-117, 25.5], scale: 0.35 },
|
|
7616
|
+
// Hawaii.
|
|
7617
|
+
{ box: [-161, 18.5, -154.5, 22.5], center: [-157.5, 20.5], to: [-106, 25.5], scale: 1 }
|
|
7618
|
+
];
|
|
7619
|
+
function insetUsPoint(lon, lat) {
|
|
7620
|
+
for (const inset of US_INSETS) {
|
|
7621
|
+
const [w, s, e, n] = inset.box;
|
|
7622
|
+
if (lon >= w && lon <= e && lat >= s && lat <= n) {
|
|
7623
|
+
return [inset.to[0] + (lon - inset.center[0]) * inset.scale, inset.to[1] + (lat - inset.center[1]) * inset.scale];
|
|
7624
|
+
}
|
|
7625
|
+
}
|
|
7626
|
+
return [lon, lat];
|
|
7627
|
+
}
|
|
7628
|
+
function insetUs(collection) {
|
|
7629
|
+
const moveRing = (ring) => ring.map(([lon, lat]) => insetUsPoint(lon, lat));
|
|
7630
|
+
return {
|
|
7631
|
+
type: "FeatureCollection",
|
|
7632
|
+
features: collection.features.map((f) => {
|
|
7633
|
+
if (f.properties.code !== "US-AK" && f.properties.code !== "US-HI") return f;
|
|
7634
|
+
const g = f.geometry;
|
|
7635
|
+
const geometry = g.type === "Polygon" ? { type: "Polygon", coordinates: g.coordinates.map(moveRing) } : { type: "MultiPolygon", coordinates: g.coordinates.map((poly) => poly.map(moveRing)) };
|
|
7636
|
+
return { ...f, geometry };
|
|
7637
|
+
})
|
|
7638
|
+
};
|
|
7639
|
+
}
|
|
7640
|
+
var loading = /* @__PURE__ */ new Map();
|
|
7641
|
+
var loaded = /* @__PURE__ */ new Map();
|
|
7642
|
+
function loadedGeoMap(name) {
|
|
7643
|
+
return loaded.get(name);
|
|
7644
|
+
}
|
|
7645
|
+
function loadGeoMap(name) {
|
|
7646
|
+
const pending = loading.get(name);
|
|
7647
|
+
if (pending) return pending;
|
|
7648
|
+
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) => {
|
|
7649
|
+
registerMap(geoMapKey(name), collection);
|
|
7650
|
+
loaded.set(name, collection);
|
|
7651
|
+
return collection;
|
|
7652
|
+
});
|
|
7653
|
+
promise.catch(() => loading.delete(name));
|
|
7654
|
+
loading.set(name, promise);
|
|
7655
|
+
return promise;
|
|
7656
|
+
}
|
|
7657
|
+
async function loadGeoMapFor(values, map) {
|
|
7658
|
+
if (map !== "auto") return { name: map, collection: await loadGeoMap(map) };
|
|
7659
|
+
const distinct = [...new Set(values.filter((v) => v !== null && v !== void 0).map(String))];
|
|
7660
|
+
const [japan, us] = await Promise.all([loadGeoMap("japan"), loadGeoMap("us")]);
|
|
7661
|
+
const local = { japan, us };
|
|
7662
|
+
const pick = detectGeoMap(distinct, local);
|
|
7663
|
+
if (pick) {
|
|
7664
|
+
const index = buildGeoIndex(pick, local[pick]);
|
|
7665
|
+
const matched = distinct.filter((v) => matchRegion(index, v) !== null).length;
|
|
7666
|
+
if (matched * 2 >= distinct.length) return { name: pick, collection: local[pick] };
|
|
7667
|
+
}
|
|
7668
|
+
const world = await loadGeoMap("world");
|
|
7669
|
+
const name = detectGeoMap(distinct, { ...local, world }) ?? "world";
|
|
7670
|
+
return { name, collection: name === "world" ? world : local[name] };
|
|
7671
|
+
}
|
|
7672
|
+
function useGeoMap(values, map) {
|
|
7673
|
+
const key = useMemo(() => [...new Set(values.map((v) => v === null || v === void 0 ? "" : String(v)))].join("\0"), [values]);
|
|
7674
|
+
const [state, setState] = useState(null);
|
|
7675
|
+
const wanted = `${map}${key}`;
|
|
7676
|
+
useEffect(() => {
|
|
7677
|
+
let alive = true;
|
|
7678
|
+
loadGeoMapFor(key ? key.split("\0") : [], map).then(
|
|
7679
|
+
(result) => alive && setState({ key: wanted, result }),
|
|
7680
|
+
() => {
|
|
7681
|
+
}
|
|
7682
|
+
);
|
|
7683
|
+
return () => {
|
|
7684
|
+
alive = false;
|
|
7685
|
+
};
|
|
7686
|
+
}, [wanted]);
|
|
7687
|
+
if (state?.key === wanted) return state.result;
|
|
7688
|
+
const ready = map !== "auto" ? loadedGeoMap(map) : void 0;
|
|
7689
|
+
return ready && map !== "auto" ? { name: map, collection: ready } : null;
|
|
7690
|
+
}
|
|
7691
|
+
use([MapChart]);
|
|
7692
|
+
function buildFilledMapModel(config) {
|
|
7693
|
+
const { data, region, value, map, collection } = config;
|
|
7694
|
+
const language = uiLanguage(resolveLocale(config.locale));
|
|
7695
|
+
const index = buildGeoIndex(map, collection);
|
|
7696
|
+
const byCode = /* @__PURE__ */ new Map();
|
|
7697
|
+
const unmatched = [];
|
|
7698
|
+
for (const row of data) {
|
|
7699
|
+
if (!row || typeof row !== "object") continue;
|
|
7700
|
+
const code = matchRegion(index, row[region]);
|
|
7701
|
+
if (code) byCode.set(code, row);
|
|
7702
|
+
else unmatched.push(row);
|
|
7703
|
+
}
|
|
7704
|
+
let lo = Infinity;
|
|
7705
|
+
let hi = -Infinity;
|
|
7706
|
+
const regions = collection.features.map(({ properties }) => {
|
|
7707
|
+
const row = byCode.get(properties.code) ?? null;
|
|
7708
|
+
const n = row ? toChartNumber(row[value]) : null;
|
|
7709
|
+
if (n !== null) [lo, hi] = [Math.min(lo, n), Math.max(hi, n)];
|
|
7710
|
+
const key = row ? String(row[region]) : null;
|
|
7711
|
+
return { code: properties.code, key, name: (key !== null ? config.labels?.[key] : void 0) ?? regionName(properties, language), value: n, row };
|
|
7712
|
+
});
|
|
7713
|
+
const min = config.scale?.min ?? (Number.isFinite(lo) ? lo : 0);
|
|
7714
|
+
let max = config.scale?.max ?? (Number.isFinite(hi) ? hi : 1);
|
|
7715
|
+
if (max <= min) max = min + 1;
|
|
7716
|
+
const selectedCodes = config.selected && config.selected.length > 0 ? new Set(config.selected.map((v) => matchRegion(index, v)).filter((c) => c !== null)) : null;
|
|
7717
|
+
return { map, regions, unmatched, selectedCodes, min, max };
|
|
7718
|
+
}
|
|
7719
|
+
function escapeHtml4(text) {
|
|
7720
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7721
|
+
}
|
|
7722
|
+
var TEXT20 = {
|
|
7723
|
+
ja: {
|
|
7724
|
+
join: "\u30FB",
|
|
7725
|
+
region: "\u5730\u57DF",
|
|
7726
|
+
notOnMap: "(\u5730\u56F3\u306B\u7121\u3044)",
|
|
7727
|
+
top: (name, value) => `\u6700\u5927\u306F ${name} \u306E ${value}`,
|
|
7728
|
+
chart: (value) => `${value}\u306E\u5730\u56F3`
|
|
7729
|
+
},
|
|
7730
|
+
en: {
|
|
7731
|
+
join: ", ",
|
|
7732
|
+
region: "Region",
|
|
7733
|
+
notOnMap: " (not on the map)",
|
|
7734
|
+
top: (name, value) => `Highest: ${name} (${value})`,
|
|
7735
|
+
chart: (value) => `Map of ${value}`
|
|
7736
|
+
}
|
|
7737
|
+
};
|
|
7738
|
+
var textOf7 = (locale) => TEXT20[uiLanguage(resolveLocale(locale))];
|
|
7739
|
+
function detailText(raw, formatter) {
|
|
7740
|
+
if (raw === null || raw === void 0 || raw === "") return EMPTY_VALUE;
|
|
7741
|
+
const n = typeof raw === "string" ? null : toChartNumber(raw);
|
|
7742
|
+
return n === null ? String(raw) : formatter(n);
|
|
7743
|
+
}
|
|
7744
|
+
function buildFilledMapOption(config) {
|
|
7745
|
+
const { model, labels, valueFormatter } = config;
|
|
7746
|
+
const t = textOf7(config.locale);
|
|
7747
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7748
|
+
const base = config.color || "#6366f1";
|
|
7749
|
+
const border = config.surfaceColor ?? "#ffffff";
|
|
7750
|
+
const dimmed = (r) => model.selectedCodes !== null && !model.selectedCodes.has(r.code);
|
|
7751
|
+
const tooltip = (r) => {
|
|
7752
|
+
const head = `${escapeHtml4(r.name)} ${r.value === null ? EMPTY_VALUE : escapeHtml4(valueFormatter(r.value))}`;
|
|
7753
|
+
const extras = (config.details ?? []).map((key) => `${escapeHtml4(label2(key))} ${escapeHtml4(detailText(r.row?.[key], valueFormatter))}`).join(t.join);
|
|
7754
|
+
return extras ? `${head}${t.join}${extras}` : head;
|
|
7755
|
+
};
|
|
7756
|
+
const option = {
|
|
7757
|
+
animation: false,
|
|
7758
|
+
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.region) } : { show: false },
|
|
7759
|
+
series: [
|
|
7760
|
+
{
|
|
7761
|
+
id: "regions",
|
|
7762
|
+
type: "map",
|
|
7763
|
+
map: geoMapKey(model.map),
|
|
7764
|
+
nameProperty: "code",
|
|
7765
|
+
aspectScale: GEO_ASPECT_SCALE[model.map],
|
|
7766
|
+
roam: false,
|
|
7767
|
+
selectedMode: false,
|
|
7768
|
+
left: 8,
|
|
7769
|
+
right: 8,
|
|
7770
|
+
top: 8,
|
|
7771
|
+
bottom: 8,
|
|
7772
|
+
label: { show: false },
|
|
7773
|
+
itemStyle: { areaColor: config.emptyColor, borderColor: border, borderWidth: 0.6 },
|
|
7774
|
+
emphasis: { label: { show: false }, itemStyle: { borderColor: config.hoverColor, borderWidth: 1.2 } },
|
|
7775
|
+
data: model.regions.map((r) => {
|
|
7776
|
+
const fill = r.value === null ? config.emptyColor : rampColor(base, config.surfaceColor, rampRatio(r.value, model.min, model.max));
|
|
7777
|
+
const areaColor = dimmed(r) ? mixIntoSurface2(fill, config.surfaceColor, 0.3) : fill;
|
|
7778
|
+
return {
|
|
7779
|
+
name: r.code,
|
|
7780
|
+
value: r.value ?? void 0,
|
|
7781
|
+
region: r,
|
|
7782
|
+
itemStyle: { areaColor },
|
|
7783
|
+
emphasis: { itemStyle: { areaColor } }
|
|
7784
|
+
};
|
|
7785
|
+
})
|
|
7786
|
+
}
|
|
7787
|
+
]
|
|
7788
|
+
};
|
|
7789
|
+
return config.chartOptions ? mergeDeep3(option, config.chartOptions) : option;
|
|
7790
|
+
}
|
|
7791
|
+
function mergeDeep3(a, b) {
|
|
7792
|
+
const out = { ...a };
|
|
7793
|
+
for (const [k, v] of Object.entries(b)) {
|
|
7794
|
+
const cur = out[k];
|
|
7795
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep3(cur, v) : v;
|
|
7796
|
+
}
|
|
7797
|
+
return out;
|
|
7798
|
+
}
|
|
7799
|
+
function filledMapDataTable(model, names, formatter, details = [], labels, locale) {
|
|
7800
|
+
const t = textOf7(locale);
|
|
7801
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7802
|
+
const cells = (row, value) => [
|
|
7803
|
+
value === null ? EMPTY_VALUE : formatter(value),
|
|
7804
|
+
...details.map((key) => detailText(row?.[key], formatter))
|
|
7805
|
+
];
|
|
7806
|
+
const rows = [
|
|
7807
|
+
...model.regions.filter((r) => r.row !== null).map((r) => [r.name, ...cells(r.row, r.value)]),
|
|
7808
|
+
...model.unmatched.map((row) => [`${label2(String(row[names.region] ?? EMPTY_VALUE))}${t.notOnMap}`, ...cells(row, toChartNumber(row[names.value]))])
|
|
7809
|
+
];
|
|
7810
|
+
return { columns: [labels?.[names.region] ?? t.region, label2(names.value), ...details.map(label2)], rows };
|
|
7811
|
+
}
|
|
7812
|
+
function buildFilledMapSummary(model, formatter, title, locale) {
|
|
7813
|
+
let best;
|
|
7814
|
+
for (const r of model.regions) if (r.value !== null && (!best || r.value > best.value)) best = r;
|
|
7815
|
+
if (!best) return void 0;
|
|
7816
|
+
return `${title ? `${title}: ` : ""}${textOf7(locale).top(best.name, formatter(best.value))}`;
|
|
7817
|
+
}
|
|
7818
|
+
function colorsFor2(tokens, tone) {
|
|
7819
|
+
const [color = "", empty = "", hover = ""] = resolveChartColors([tone, "var(--muted)", "var(--foreground)"], tokens);
|
|
7820
|
+
return { color, empty, hover };
|
|
7821
|
+
}
|
|
7822
|
+
var NO_ROWS15 = [];
|
|
7823
|
+
function FilledMapImpl({
|
|
7824
|
+
data = NO_ROWS15,
|
|
7825
|
+
region,
|
|
7826
|
+
value,
|
|
7827
|
+
map = "auto",
|
|
7828
|
+
labels,
|
|
7829
|
+
format,
|
|
7830
|
+
valueFormatter,
|
|
7831
|
+
tone = "chart-1",
|
|
7832
|
+
scale,
|
|
7833
|
+
showLegend = true,
|
|
7834
|
+
showTooltip = true,
|
|
7835
|
+
details,
|
|
7836
|
+
selected,
|
|
7837
|
+
onSelect,
|
|
7838
|
+
title,
|
|
7839
|
+
summary,
|
|
7840
|
+
isLoading,
|
|
7841
|
+
emptyState,
|
|
7842
|
+
height,
|
|
7843
|
+
chartOptions,
|
|
7844
|
+
actions,
|
|
7845
|
+
fileName,
|
|
7846
|
+
className,
|
|
7847
|
+
ariaLabel,
|
|
7848
|
+
...rest
|
|
7849
|
+
}, ref) {
|
|
7850
|
+
const rows = Array.isArray(data) ? data : NO_ROWS15;
|
|
7851
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
7852
|
+
const locale = useLocale();
|
|
7853
|
+
const formatter = useMemo(() => resolveValueFormatter(valueFormatter, format ?? "number", locale), [valueFormatter, format, locale]);
|
|
7854
|
+
const regionValues = useMemo(() => rows.map((row) => row && typeof row === "object" ? row[region] : void 0), [rows, region]);
|
|
7855
|
+
const geo = useGeoMap(regionValues, map);
|
|
7856
|
+
const model = useMemo(
|
|
7857
|
+
() => geo ? buildFilledMapModel({ data: rows, region, value, map: geo.name, collection: geo.collection, scale, labels, selected, locale }) : null,
|
|
7858
|
+
[geo, rows, region, value, scale?.min, scale?.max, labels, selected, locale]
|
|
7859
|
+
);
|
|
7860
|
+
const colors = useMemo(() => colorsFor2(tokens, tone), [tokens, tone]);
|
|
7861
|
+
const option = useMemo(
|
|
7862
|
+
() => model ? buildFilledMapOption({
|
|
7863
|
+
model,
|
|
7864
|
+
color: colors.color,
|
|
7865
|
+
surfaceColor: surface ?? void 0,
|
|
7866
|
+
emptyColor: colors.empty || "rgba(128, 128, 128, 0.15)",
|
|
7867
|
+
hoverColor: colors.hover || "#000000",
|
|
7868
|
+
labels,
|
|
7869
|
+
valueFormatter: formatter,
|
|
7870
|
+
details,
|
|
7871
|
+
showTooltip,
|
|
7872
|
+
chartOptions,
|
|
7873
|
+
locale
|
|
7874
|
+
}) : null,
|
|
7875
|
+
[model, value, colors, surface, labels, formatter, details, showTooltip, chartOptions, locale]
|
|
7876
|
+
);
|
|
7877
|
+
const onEvents = useMemo(
|
|
7878
|
+
() => onSelect ? {
|
|
7879
|
+
click: (params) => {
|
|
7880
|
+
const r = params.data?.region;
|
|
7881
|
+
if (r?.row) onSelect(String(r.row[region]), r.row);
|
|
7882
|
+
}
|
|
7883
|
+
} : void 0,
|
|
7884
|
+
[onSelect, region]
|
|
7885
|
+
);
|
|
7886
|
+
const dataTable = useMemo(
|
|
7887
|
+
() => model ? filledMapDataTable(model, { region, value }, formatter, details, labels, locale) : void 0,
|
|
7888
|
+
[model, region, value, formatter, details, labels, locale]
|
|
7889
|
+
);
|
|
7890
|
+
const resolvedSummary = useMemo(
|
|
7891
|
+
() => summary === "auto" ? model ? buildFilledMapSummary(model, formatter, title, locale) : void 0 : summary,
|
|
7892
|
+
[summary, model, formatter, title, locale]
|
|
7893
|
+
);
|
|
7894
|
+
const label2 = ariaLabel ?? textOf7(locale).chart(labels?.[value] ?? value ?? "");
|
|
7895
|
+
const isEmpty = rows.length === 0;
|
|
7896
|
+
return /* @__PURE__ */ jsx(
|
|
7897
|
+
ChartFrame,
|
|
7898
|
+
{
|
|
7899
|
+
...chartRootProps(rest),
|
|
7900
|
+
ref: frameRef,
|
|
7901
|
+
title,
|
|
7902
|
+
summary: resolvedSummary,
|
|
7903
|
+
isLoading: (isLoading ?? false) || !isEmpty && !option,
|
|
7904
|
+
isEmpty,
|
|
7905
|
+
emptyState,
|
|
7906
|
+
height,
|
|
7907
|
+
variant: "bar",
|
|
7908
|
+
ariaLabel: label2,
|
|
7909
|
+
className,
|
|
7910
|
+
dataTable,
|
|
7911
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
7912
|
+
/* @__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 } }) }),
|
|
7913
|
+
showLegend && model ? /* @__PURE__ */ jsx(RampLegend, { min: model.min, max: model.max, formatter, tone }) : null
|
|
7914
|
+
] })
|
|
7915
|
+
}
|
|
7916
|
+
);
|
|
7917
|
+
}
|
|
7918
|
+
function RampLegend({ min, max, formatter, tone }) {
|
|
7919
|
+
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: [
|
|
7920
|
+
/* @__PURE__ */ jsx("span", { children: formatter(min) }),
|
|
7921
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
7922
|
+
/* @__PURE__ */ jsx("span", { children: formatter(max) })
|
|
7923
|
+
] }) });
|
|
7924
|
+
}
|
|
7925
|
+
var FilledMap = forwardRef(FilledMapImpl);
|
|
7926
|
+
FilledMap.displayName = "FilledMap";
|
|
7927
|
+
use([ScatterChart$1, GeoComponent]);
|
|
7928
|
+
var SYMBOL_MAP_MIN_SIZE = 6;
|
|
7929
|
+
var SYMBOL_MAP_MAX_SIZE = 36;
|
|
7930
|
+
var SYMBOL_MAP_DEFAULT_SIZE = 10;
|
|
7931
|
+
var MAX_CATEGORIES = 5;
|
|
7932
|
+
var isBlank = (v) => v === null || v === void 0 || typeof v === "string" && v.trim() === "";
|
|
7933
|
+
function buildSymbolMapModel(config) {
|
|
7934
|
+
const { data, latitude, longitude, name, size, color } = config;
|
|
7935
|
+
const rows = data.filter((row) => row && typeof row === "object");
|
|
7936
|
+
const colorValues = color ? rows.map((row) => row[color]).filter((v) => !isBlank(v)) : [];
|
|
7937
|
+
const colorMode = !color || colorValues.length === 0 ? "none" : colorValues.every((v) => toChartNumber(v) !== null) ? "numeric" : "category";
|
|
7938
|
+
const points = [];
|
|
7939
|
+
const unplotted = [];
|
|
7940
|
+
const categories = [];
|
|
7941
|
+
let [lo, hi, sizeMax] = [Infinity, -Infinity, 0];
|
|
7942
|
+
for (const row of rows) {
|
|
7943
|
+
const lat = toChartNumber(row[latitude]);
|
|
7944
|
+
const lon = toChartNumber(row[longitude]);
|
|
7945
|
+
if (lat === null || lon === null || Math.abs(lat) > 90 || Math.abs(lon) > 180) {
|
|
7946
|
+
unplotted.push(row);
|
|
7947
|
+
continue;
|
|
7948
|
+
}
|
|
7949
|
+
const s = size ? toChartNumber(row[size]) : null;
|
|
7950
|
+
if (s !== null && s > sizeMax) sizeMax = s;
|
|
7951
|
+
const shade = colorMode === "numeric" ? toChartNumber(row[color]) : null;
|
|
7952
|
+
if (shade !== null) [lo, hi] = [Math.min(lo, shade), Math.max(hi, shade)];
|
|
7953
|
+
const category = colorMode === "category" && !isBlank(row[color]) ? String(row[color]) : null;
|
|
7954
|
+
if (category !== null && !categories.includes(category)) categories.push(category);
|
|
7955
|
+
points.push({ lon, lat, name: name && !isBlank(row[name]) ? String(row[name]) : null, size: s, shade, category, row });
|
|
7956
|
+
}
|
|
7957
|
+
const shadeMin = Number.isFinite(lo) ? lo : 0;
|
|
7958
|
+
let shadeMax = Number.isFinite(hi) ? hi : 1;
|
|
7959
|
+
if (shadeMax <= shadeMin) shadeMax = shadeMin + 1;
|
|
7960
|
+
return { points, unplotted, colorMode, categories, shadeMin, shadeMax, sizeMax };
|
|
7961
|
+
}
|
|
7962
|
+
function symbolMapSize(value, max, hasSize) {
|
|
7963
|
+
if (!hasSize) return SYMBOL_MAP_DEFAULT_SIZE;
|
|
7964
|
+
if (value === null || value <= 0 || !(max > 0)) return SYMBOL_MAP_MIN_SIZE;
|
|
7965
|
+
return SYMBOL_MAP_MIN_SIZE + (SYMBOL_MAP_MAX_SIZE - SYMBOL_MAP_MIN_SIZE) * Math.sqrt(Math.min(1, value / max));
|
|
7966
|
+
}
|
|
7967
|
+
function escapeHtml5(text) {
|
|
7968
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
7969
|
+
}
|
|
7970
|
+
var TEXT21 = {
|
|
7971
|
+
ja: {
|
|
7972
|
+
join: "\u30FB",
|
|
7973
|
+
point: "\u5730\u70B9",
|
|
7974
|
+
latitude: "\u7DEF\u5EA6",
|
|
7975
|
+
longitude: "\u7D4C\u5EA6",
|
|
7976
|
+
notPlotted: "(\u5EA7\u6A19\u306A\u3057)",
|
|
7977
|
+
chart: "\u5730\u70B9\u306E\u5730\u56F3"
|
|
7978
|
+
},
|
|
7979
|
+
en: {
|
|
7980
|
+
join: ", ",
|
|
7981
|
+
point: "Point",
|
|
7982
|
+
latitude: "Latitude",
|
|
7983
|
+
longitude: "Longitude",
|
|
7984
|
+
notPlotted: " (no coordinates)",
|
|
7985
|
+
chart: "Map of points"
|
|
7986
|
+
}
|
|
7987
|
+
};
|
|
7988
|
+
var textOf8 = (locale) => TEXT21[uiLanguage(resolveLocale(locale))];
|
|
7989
|
+
var coordText = (n) => n === null ? EMPTY_VALUE : n.toFixed(4).replace(/\.?0+$/, "");
|
|
7990
|
+
function buildSymbolMapOption(config) {
|
|
7991
|
+
const { model, labels, valueFormatter, sizeFormatter } = config;
|
|
7992
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
7993
|
+
const base = config.toneColor || "#6366f1";
|
|
7994
|
+
const selected = config.selected && config.selected.length > 0 ? new Set(config.selected.map(String)) : null;
|
|
7995
|
+
const fillOf = (p) => {
|
|
7996
|
+
if (model.colorMode === "numeric") return p.shade === null ? config.landColor : rampColor(base, config.surfaceColor, rampRatio(p.shade, model.shadeMin, model.shadeMax));
|
|
7997
|
+
if (model.colorMode === "category") {
|
|
7998
|
+
const at = p.category === null ? -1 : model.categories.indexOf(p.category);
|
|
7999
|
+
return at === -1 ? config.categoryColors[MAX_CATEGORIES] ?? base : config.categoryColors[Math.min(at, MAX_CATEGORIES)] ?? base;
|
|
8000
|
+
}
|
|
8001
|
+
return base;
|
|
8002
|
+
};
|
|
8003
|
+
const tooltip = (p) => {
|
|
8004
|
+
const lines = [escapeHtml5(p.name ?? `${coordText(p.lat)}, ${coordText(p.lon)}`)];
|
|
8005
|
+
if (config.size) lines.push(`${escapeHtml5(label2(config.size))} ${p.size === null ? EMPTY_VALUE : escapeHtml5(sizeFormatter(p.size))}`);
|
|
8006
|
+
if (config.color && config.color !== config.size)
|
|
8007
|
+
lines.push(`${escapeHtml5(label2(config.color))} ${model.colorMode === "numeric" ? p.shade === null ? EMPTY_VALUE : escapeHtml5(valueFormatter(p.shade)) : escapeHtml5(label2(p.category ?? EMPTY_VALUE))}`);
|
|
8008
|
+
for (const key of config.details ?? []) lines.push(`${escapeHtml5(label2(key))} ${escapeHtml5(cellText(p.row[key], valueFormatter))}`);
|
|
8009
|
+
return lines.join("<br/>");
|
|
8010
|
+
};
|
|
8011
|
+
const order = [...model.points].sort((a, b) => (b.size ?? 0) - (a.size ?? 0));
|
|
8012
|
+
const option = {
|
|
8013
|
+
animation: false,
|
|
8014
|
+
tooltip: config.showTooltip ? { trigger: "item", formatter: (p) => tooltip(p.data.point) } : { show: false },
|
|
8015
|
+
geo: {
|
|
8016
|
+
map: geoMapKey(config.basemap),
|
|
8017
|
+
silent: true,
|
|
8018
|
+
roam: false,
|
|
8019
|
+
aspectScale: GEO_ASPECT_SCALE[config.basemap],
|
|
8020
|
+
left: 8,
|
|
8021
|
+
right: 8,
|
|
8022
|
+
top: 8,
|
|
8023
|
+
bottom: 8,
|
|
8024
|
+
label: { show: false },
|
|
8025
|
+
itemStyle: { areaColor: config.landColor, borderColor: config.borderColor, borderWidth: 0.6 },
|
|
8026
|
+
emphasis: { disabled: true }
|
|
8027
|
+
},
|
|
8028
|
+
series: [
|
|
8029
|
+
{
|
|
8030
|
+
id: "points",
|
|
8031
|
+
type: "scatter",
|
|
8032
|
+
coordinateSystem: "geo",
|
|
8033
|
+
data: order.map((p) => {
|
|
8034
|
+
const [x, y] = config.basemap === "us" ? insetUsPoint(p.lon, p.lat) : [p.lon, p.lat];
|
|
8035
|
+
const fill = fillOf(p);
|
|
8036
|
+
const dim = selected !== null && !(p.name !== null && selected.has(p.name));
|
|
8037
|
+
return {
|
|
8038
|
+
value: [x, y],
|
|
8039
|
+
point: p,
|
|
8040
|
+
symbolSize: symbolMapSize(p.size, model.sizeMax, !!config.size),
|
|
8041
|
+
itemStyle: { color: dim ? mixIntoSurface2(fill, config.surfaceColor, 0.3) : fill, opacity: dim ? 1 : 0.85 }
|
|
8042
|
+
};
|
|
8043
|
+
}),
|
|
8044
|
+
itemStyle: { borderColor: config.surfaceColor ?? "#ffffff", borderWidth: 1 },
|
|
8045
|
+
emphasis: { scale: false, itemStyle: { opacity: 1, borderColor: config.borderColor, borderWidth: 1.5 } }
|
|
8046
|
+
}
|
|
8047
|
+
]
|
|
8048
|
+
};
|
|
8049
|
+
return config.chartOptions ? mergeDeep4(option, config.chartOptions) : option;
|
|
8050
|
+
}
|
|
8051
|
+
function cellText(raw, formatter) {
|
|
8052
|
+
if (isBlank(raw)) return EMPTY_VALUE;
|
|
8053
|
+
const n = typeof raw === "string" ? null : toChartNumber(raw);
|
|
8054
|
+
return n === null ? String(raw) : formatter(n);
|
|
8055
|
+
}
|
|
8056
|
+
function mergeDeep4(a, b) {
|
|
8057
|
+
const out = { ...a };
|
|
8058
|
+
for (const [k, v] of Object.entries(b)) {
|
|
8059
|
+
const cur = out[k];
|
|
8060
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep4(cur, v) : v;
|
|
8061
|
+
}
|
|
8062
|
+
return out;
|
|
8063
|
+
}
|
|
8064
|
+
function symbolMapDataTable(model, names, formatters, details = [], labels, locale) {
|
|
8065
|
+
const t = textOf8(locale);
|
|
8066
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8067
|
+
const extra = [names.size, names.color].filter((k) => !!k && k !== names.latitude && k !== names.longitude);
|
|
8068
|
+
const keys = [.../* @__PURE__ */ new Set([...extra, ...details])];
|
|
8069
|
+
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));
|
|
8070
|
+
const nameOf = (row) => names.name && !isBlank(row[names.name]) ? String(row[names.name]) : EMPTY_VALUE;
|
|
8071
|
+
const rows = [
|
|
8072
|
+
...model.points.map((p) => [nameOf(p.row), coordText(p.lat), coordText(p.lon), ...rowCells(p.row)]),
|
|
8073
|
+
...model.unplotted.map((row) => [
|
|
8074
|
+
`${nameOf(row)}${t.notPlotted}`,
|
|
8075
|
+
coordText(toChartNumber(row[names.latitude])),
|
|
8076
|
+
coordText(toChartNumber(row[names.longitude])),
|
|
8077
|
+
...rowCells(row)
|
|
8078
|
+
])
|
|
8079
|
+
];
|
|
8080
|
+
return {
|
|
8081
|
+
columns: [names.name ? label2(names.name) : t.point, labels?.[names.latitude] ?? t.latitude, labels?.[names.longitude] ?? t.longitude, ...keys.map(label2)],
|
|
8082
|
+
rows
|
|
8083
|
+
};
|
|
8084
|
+
}
|
|
8085
|
+
function colorsFor3(tokens, tone) {
|
|
8086
|
+
const resolved = resolveChartColors([tone, "var(--muted)", "var(--border)", ...DEFAULT_CHART_COLORS, CHART_OTHER_COLOR], tokens);
|
|
8087
|
+
const [toneColor = "", land = "", border = ""] = resolved;
|
|
8088
|
+
return { toneColor, land, border, categories: resolved.slice(3) };
|
|
8089
|
+
}
|
|
8090
|
+
var NO_ROWS16 = [];
|
|
8091
|
+
function SymbolMapImpl({
|
|
8092
|
+
data = NO_ROWS16,
|
|
8093
|
+
latitude,
|
|
8094
|
+
longitude,
|
|
8095
|
+
name,
|
|
8096
|
+
size,
|
|
8097
|
+
color,
|
|
8098
|
+
basemap = "auto",
|
|
8099
|
+
labels,
|
|
8100
|
+
format,
|
|
8101
|
+
sizeFormat,
|
|
8102
|
+
tone = "chart-1",
|
|
8103
|
+
showLegend = true,
|
|
8104
|
+
showTooltip = true,
|
|
8105
|
+
details,
|
|
8106
|
+
selected,
|
|
8107
|
+
onSelect,
|
|
8108
|
+
title,
|
|
8109
|
+
summary,
|
|
8110
|
+
isLoading,
|
|
8111
|
+
emptyState,
|
|
8112
|
+
height,
|
|
8113
|
+
chartOptions,
|
|
8114
|
+
actions,
|
|
8115
|
+
fileName,
|
|
8116
|
+
className,
|
|
8117
|
+
ariaLabel,
|
|
8118
|
+
...rest
|
|
8119
|
+
}, ref) {
|
|
8120
|
+
const rows = Array.isArray(data) ? data : NO_ROWS16;
|
|
8121
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
8122
|
+
const locale = useLocale();
|
|
8123
|
+
const formatter = useMemo(() => resolveValueFormatter(void 0, format ?? "number", locale), [format, locale]);
|
|
8124
|
+
const sizeFormatter = useMemo(() => resolveValueFormatter(void 0, sizeFormat ?? format ?? "number", locale), [sizeFormat, format, locale]);
|
|
8125
|
+
const model = useMemo(() => buildSymbolMapModel({ data: rows, latitude, longitude, name, size, color }), [rows, latitude, longitude, name, size, color]);
|
|
8126
|
+
const resolvedBasemap = useMemo(
|
|
8127
|
+
() => basemap === "auto" ? detectBasemap(model.points.map((p) => [p.lon, p.lat])) : basemap,
|
|
8128
|
+
[basemap, model]
|
|
8129
|
+
);
|
|
8130
|
+
const geo = useGeoMap(NO_ROWS16, resolvedBasemap);
|
|
8131
|
+
const colors = useMemo(() => colorsFor3(tokens, tone), [tokens, tone]);
|
|
8132
|
+
const option = useMemo(
|
|
8133
|
+
() => geo ? buildSymbolMapOption({
|
|
8134
|
+
model,
|
|
8135
|
+
basemap: geo.name,
|
|
8136
|
+
size,
|
|
8137
|
+
color,
|
|
8138
|
+
toneColor: colors.toneColor,
|
|
8139
|
+
categoryColors: colors.categories,
|
|
8140
|
+
surfaceColor: surface ?? void 0,
|
|
8141
|
+
landColor: colors.land || "rgba(128, 128, 128, 0.15)",
|
|
8142
|
+
borderColor: colors.border || "rgba(128, 128, 128, 0.4)",
|
|
8143
|
+
labels,
|
|
8144
|
+
valueFormatter: formatter,
|
|
8145
|
+
sizeFormatter,
|
|
8146
|
+
details,
|
|
8147
|
+
selected,
|
|
8148
|
+
showTooltip,
|
|
8149
|
+
chartOptions}) : null,
|
|
8150
|
+
[geo, model, size, color, colors, surface, labels, formatter, sizeFormatter, details, selected, showTooltip, chartOptions, locale]
|
|
8151
|
+
);
|
|
8152
|
+
const onEvents = useMemo(
|
|
8153
|
+
() => onSelect ? {
|
|
8154
|
+
click: (params) => {
|
|
8155
|
+
const p = params.data?.point;
|
|
8156
|
+
if (p) onSelect(p.row);
|
|
8157
|
+
}
|
|
8158
|
+
} : void 0,
|
|
8159
|
+
[onSelect]
|
|
8160
|
+
);
|
|
8161
|
+
const dataTable = useMemo(
|
|
8162
|
+
() => symbolMapDataTable(model, { latitude, longitude, name, size, color }, { value: formatter, size: sizeFormatter }, details, labels, locale),
|
|
8163
|
+
[model, latitude, longitude, name, size, color, formatter, sizeFormatter, details, labels, locale]
|
|
8164
|
+
);
|
|
8165
|
+
const label2 = ariaLabel ?? textOf8(locale).chart;
|
|
8166
|
+
const isEmpty = rows.length === 0;
|
|
8167
|
+
const legendCategories = model.categories.length > MAX_CATEGORIES ? [...model.categories.slice(0, MAX_CATEGORIES), otherSeriesLabel(locale)] : model.categories;
|
|
8168
|
+
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: [
|
|
8169
|
+
color && /* @__PURE__ */ jsx("span", { children: labels?.[color] ?? color }),
|
|
8170
|
+
/* @__PURE__ */ jsx("span", { children: formatter(model.shadeMin) }),
|
|
8171
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
8172
|
+
/* @__PURE__ */ jsx("span", { children: formatter(model.shadeMax) })
|
|
8173
|
+
] }) }) : model.colorMode === "category" ? /* @__PURE__ */ jsx(Legend, { categories: legendCategories, labels }) : null;
|
|
8174
|
+
return /* @__PURE__ */ jsx(
|
|
8175
|
+
ChartFrame,
|
|
8176
|
+
{
|
|
8177
|
+
...chartRootProps(rest),
|
|
8178
|
+
ref: frameRef,
|
|
8179
|
+
title,
|
|
8180
|
+
summary,
|
|
8181
|
+
isLoading: (isLoading ?? false) || !isEmpty && !option,
|
|
8182
|
+
isEmpty,
|
|
8183
|
+
emptyState,
|
|
8184
|
+
height,
|
|
8185
|
+
variant: "bar",
|
|
8186
|
+
ariaLabel: label2,
|
|
8187
|
+
className,
|
|
8188
|
+
dataTable,
|
|
8189
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
8190
|
+
/* @__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 } }) }),
|
|
8191
|
+
legend
|
|
8192
|
+
] })
|
|
8193
|
+
}
|
|
8194
|
+
);
|
|
8195
|
+
}
|
|
8196
|
+
var SymbolMap = forwardRef(SymbolMapImpl);
|
|
8197
|
+
SymbolMap.displayName = "SymbolMap";
|
|
8198
|
+
use([TreemapChart]);
|
|
8199
|
+
var MAX_CATEGORIES2 = 5;
|
|
8200
|
+
function buildTreemapModel(config) {
|
|
8201
|
+
const { data, index, group, value, color } = config;
|
|
8202
|
+
const leaves = [];
|
|
8203
|
+
let [lo, hi] = [Infinity, -Infinity];
|
|
8204
|
+
for (const row of data) {
|
|
8205
|
+
if (!row || typeof row !== "object") continue;
|
|
8206
|
+
const shade = color ? toChartNumber(row[color]) : null;
|
|
8207
|
+
if (shade !== null) [lo, hi] = [Math.min(lo, shade), Math.max(hi, shade)];
|
|
8208
|
+
leaves.push({
|
|
8209
|
+
name: String(row[index] ?? EMPTY_VALUE),
|
|
8210
|
+
group: group ? String(row[group] ?? EMPTY_VALUE) : null,
|
|
8211
|
+
value: toChartNumber(row[value]),
|
|
8212
|
+
shade,
|
|
8213
|
+
row
|
|
8214
|
+
});
|
|
8215
|
+
}
|
|
8216
|
+
const byGroup = /* @__PURE__ */ new Map();
|
|
8217
|
+
for (const leaf of leaves) {
|
|
8218
|
+
if (leaf.value === null || leaf.value <= 0) continue;
|
|
8219
|
+
const list = byGroup.get(leaf.group) ?? [];
|
|
8220
|
+
list.push(leaf);
|
|
8221
|
+
byGroup.set(leaf.group, list);
|
|
8222
|
+
}
|
|
8223
|
+
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);
|
|
8224
|
+
const total = groups.reduce((s, g) => s + g.total, 0);
|
|
8225
|
+
const categories = group ? groups.map((g) => g.name) : [...new Set(groups.flatMap((g) => g.leaves.map((l) => l.name)))];
|
|
8226
|
+
const shadeMin = Number.isFinite(lo) ? lo : 0;
|
|
8227
|
+
let shadeMax = Number.isFinite(hi) ? hi : 1;
|
|
8228
|
+
if (shadeMax <= shadeMin) shadeMax = shadeMin + 1;
|
|
8229
|
+
return { leaves, groups, categories, total, shadeMin, shadeMax };
|
|
8230
|
+
}
|
|
8231
|
+
function escapeHtml6(text) {
|
|
8232
|
+
return text.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
8233
|
+
}
|
|
8234
|
+
var TEXT22 = {
|
|
8235
|
+
ja: {
|
|
8236
|
+
join: "\u30FB",
|
|
8237
|
+
item: "\u9805\u76EE",
|
|
8238
|
+
group: "\u30B0\u30EB\u30FC\u30D7",
|
|
8239
|
+
share: "\u69CB\u6210\u6BD4",
|
|
8240
|
+
top: (name, share) => `\u6700\u5927\u306F ${name}(\u5168\u4F53\u306E ${share})`,
|
|
8241
|
+
chart: (value) => `${value}\u306E\u30C4\u30EA\u30FC\u30DE\u30C3\u30D7`
|
|
8242
|
+
},
|
|
8243
|
+
en: {
|
|
8244
|
+
join: ", ",
|
|
8245
|
+
item: "Item",
|
|
8246
|
+
group: "Group",
|
|
8247
|
+
share: "Share",
|
|
8248
|
+
top: (name, share) => `Largest: ${name} (${share} of the total)`,
|
|
8249
|
+
chart: (value) => `Treemap of ${value}`
|
|
8250
|
+
}
|
|
8251
|
+
};
|
|
8252
|
+
var textOf9 = (locale) => TEXT22[uiLanguage(resolveLocale(locale))];
|
|
8253
|
+
function buildTreemapOption(config) {
|
|
8254
|
+
const { model, labels, valueFormatter } = config;
|
|
8255
|
+
const t = textOf9(config.locale);
|
|
8256
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8257
|
+
const surface = config.surfaceColor ?? "#ffffff";
|
|
8258
|
+
const base = config.toneColor || "#6366f1";
|
|
8259
|
+
const categoryColor = (name) => {
|
|
8260
|
+
const at = model.categories.indexOf(name);
|
|
8261
|
+
return config.categoryColors[at === -1 ? MAX_CATEGORIES2 : Math.min(at, MAX_CATEGORIES2)] ?? base;
|
|
8262
|
+
};
|
|
8263
|
+
const share = (v) => formatValue(model.total > 0 ? v / model.total : 0, "percent", config.locale);
|
|
8264
|
+
const leafNode = (leaf) => {
|
|
8265
|
+
let fill;
|
|
8266
|
+
let dark = true;
|
|
8267
|
+
if (config.colorMode === "numeric") {
|
|
8268
|
+
const ratio = leaf.shade === null ? 0 : rampRatio(leaf.shade, model.shadeMin, model.shadeMax);
|
|
8269
|
+
fill = rampColor(base, config.surfaceColor, ratio);
|
|
8270
|
+
dark = ratio >= RAMP_LIGHT_TEXT_FROM;
|
|
8271
|
+
} else fill = categoryColor(config.hasGroup ? leaf.group : leaf.name);
|
|
8272
|
+
return {
|
|
8273
|
+
name: label2(leaf.name),
|
|
8274
|
+
value: leaf.value,
|
|
8275
|
+
leaf,
|
|
8276
|
+
itemStyle: { color: fill },
|
|
8277
|
+
label: { color: dark ? surface : config.foregroundColor }
|
|
8278
|
+
};
|
|
8279
|
+
};
|
|
8280
|
+
const tooltip = (leaf, name, value) => {
|
|
8281
|
+
const head = leaf?.group != null ? `${escapeHtml6(label2(leaf.group))} / ${escapeHtml6(name)}` : escapeHtml6(name);
|
|
8282
|
+
const parts = [`${head} ${escapeHtml6(valueFormatter(value))}`, `${t.share} ${escapeHtml6(share(value))}`];
|
|
8283
|
+
if (leaf && config.colorKey && config.colorMode === "numeric")
|
|
8284
|
+
parts.push(`${escapeHtml6(label2(config.colorKey))} ${leaf.shade === null ? EMPTY_VALUE : escapeHtml6(config.colorFormatter(leaf.shade))}`);
|
|
8285
|
+
return parts.join(t.join);
|
|
8286
|
+
};
|
|
8287
|
+
const data = config.hasGroup ? model.groups.map((g) => ({
|
|
8288
|
+
name: label2(g.name),
|
|
8289
|
+
value: g.total,
|
|
8290
|
+
itemStyle: { color: config.colorMode === "numeric" ? config.foregroundColor : categoryColor(g.name) },
|
|
8291
|
+
children: g.leaves.map(leafNode)
|
|
8292
|
+
})) : model.groups.flatMap((g) => g.leaves.map(leafNode));
|
|
8293
|
+
const option = {
|
|
8294
|
+
animation: false,
|
|
8295
|
+
tooltip: config.showTooltip ? {
|
|
8296
|
+
trigger: "item",
|
|
8297
|
+
formatter: (p) => {
|
|
8298
|
+
const d = p;
|
|
8299
|
+
return tooltip(d.data?.leaf, d.name, Number(d.value));
|
|
8300
|
+
}
|
|
8301
|
+
} : { show: false },
|
|
8302
|
+
series: [
|
|
8303
|
+
{
|
|
8304
|
+
id: "tiles",
|
|
8305
|
+
type: "treemap",
|
|
8306
|
+
left: 0,
|
|
8307
|
+
right: 0,
|
|
8308
|
+
top: 0,
|
|
8309
|
+
bottom: 0,
|
|
8310
|
+
roam: false,
|
|
8311
|
+
nodeClick: false,
|
|
8312
|
+
breadcrumb: { show: false },
|
|
8313
|
+
squareRatio: 0.5 * (1 + Math.sqrt(5)),
|
|
8314
|
+
label: {
|
|
8315
|
+
show: true,
|
|
8316
|
+
position: "insideTopLeft",
|
|
8317
|
+
overflow: "truncate",
|
|
8318
|
+
lineHeight: 16,
|
|
8319
|
+
formatter: (p) => {
|
|
8320
|
+
const d = p;
|
|
8321
|
+
return `${d.name}
|
|
8322
|
+
${valueFormatter(Number(d.value))}`;
|
|
8323
|
+
}
|
|
8324
|
+
},
|
|
8325
|
+
upperLabel: { show: config.hasGroup, height: 20, color: surface, fontWeight: 600 },
|
|
8326
|
+
itemStyle: { borderColor: surface, borderWidth: 1, gapWidth: 1 },
|
|
8327
|
+
emphasis: { disabled: true },
|
|
8328
|
+
levels: config.hasGroup ? [{ itemStyle: { borderColor: surface, borderWidth: 2, gapWidth: 2 } }, { itemStyle: { borderColor: surface, borderWidth: 0, gapWidth: 1 } }] : [{ itemStyle: { borderColor: surface, borderWidth: 0, gapWidth: 1 } }],
|
|
8329
|
+
data
|
|
8330
|
+
}
|
|
8331
|
+
]
|
|
8332
|
+
};
|
|
8333
|
+
return config.chartOptions ? mergeDeep5(option, config.chartOptions) : option;
|
|
8334
|
+
}
|
|
8335
|
+
function mergeDeep5(a, b) {
|
|
8336
|
+
const out = { ...a };
|
|
8337
|
+
for (const [k, v] of Object.entries(b)) {
|
|
8338
|
+
const cur = out[k];
|
|
8339
|
+
out[k] = v && cur && typeof v === "object" && typeof cur === "object" && !Array.isArray(v) && !Array.isArray(cur) ? mergeDeep5(cur, v) : v;
|
|
8340
|
+
}
|
|
8341
|
+
return out;
|
|
8342
|
+
}
|
|
8343
|
+
function treemapDataTable(model, names, formatters, labels, locale) {
|
|
8344
|
+
const t = textOf9(locale);
|
|
8345
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
8346
|
+
const share = (v) => v === null || v <= 0 || model.total <= 0 ? EMPTY_VALUE : formatValue(v / model.total, "percent", locale);
|
|
8347
|
+
return {
|
|
8348
|
+
columns: [
|
|
8349
|
+
...names.group ? [labels?.[names.group] ?? t.group] : [],
|
|
8350
|
+
labels?.[names.index] ?? t.item,
|
|
8351
|
+
label2(names.value),
|
|
8352
|
+
t.share,
|
|
8353
|
+
...names.color && names.color !== names.value ? [label2(names.color)] : []
|
|
8354
|
+
],
|
|
8355
|
+
rows: model.leaves.map((leaf) => [
|
|
8356
|
+
...names.group ? [label2(leaf.group ?? EMPTY_VALUE)] : [],
|
|
8357
|
+
label2(leaf.name),
|
|
8358
|
+
leaf.value === null ? EMPTY_VALUE : formatters.value(leaf.value),
|
|
8359
|
+
share(leaf.value),
|
|
8360
|
+
...names.color && names.color !== names.value ? [leaf.shade === null ? EMPTY_VALUE : formatters.color(leaf.shade)] : []
|
|
8361
|
+
])
|
|
8362
|
+
};
|
|
8363
|
+
}
|
|
8364
|
+
function buildTreemapSummary(model, title, labels, locale) {
|
|
8365
|
+
let best;
|
|
8366
|
+
for (const g of model.groups) for (const l of g.leaves) if (!best || l.value > best.value) best = l;
|
|
8367
|
+
if (!best || model.total <= 0) return void 0;
|
|
8368
|
+
return `${title ? `${title}: ` : ""}${textOf9(locale).top(labels?.[best.name] ?? best.name, formatValue(best.value / model.total, "percent", locale))}`;
|
|
8369
|
+
}
|
|
8370
|
+
function colorsFor4(tokens, tone) {
|
|
8371
|
+
const resolved = resolveChartColors([tone, "var(--foreground)", ...DEFAULT_CHART_COLORS, CHART_OTHER_COLOR], tokens);
|
|
8372
|
+
const [toneColor = "", foreground = ""] = resolved;
|
|
8373
|
+
return { toneColor, foreground, categories: resolved.slice(2) };
|
|
8374
|
+
}
|
|
8375
|
+
var NO_ROWS17 = [];
|
|
8376
|
+
function TreemapImpl({
|
|
8377
|
+
data = NO_ROWS17,
|
|
8378
|
+
index,
|
|
8379
|
+
group,
|
|
8380
|
+
value,
|
|
8381
|
+
color,
|
|
8382
|
+
labels,
|
|
8383
|
+
format,
|
|
8384
|
+
colorFormat,
|
|
8385
|
+
valueFormatter,
|
|
8386
|
+
tone = "chart-1",
|
|
8387
|
+
showLegend = true,
|
|
8388
|
+
showTooltip = true,
|
|
8389
|
+
onSelect,
|
|
8390
|
+
title,
|
|
8391
|
+
summary,
|
|
8392
|
+
isLoading,
|
|
8393
|
+
emptyState,
|
|
8394
|
+
height,
|
|
8395
|
+
chartOptions,
|
|
8396
|
+
actions,
|
|
8397
|
+
fileName,
|
|
8398
|
+
className,
|
|
8399
|
+
ariaLabel,
|
|
8400
|
+
...rest
|
|
8401
|
+
}, ref) {
|
|
8402
|
+
const rows = Array.isArray(data) ? data : NO_ROWS17;
|
|
8403
|
+
const { ref: frameRef, surface, tokens } = useChartSurface(ref);
|
|
8404
|
+
const locale = useLocale();
|
|
8405
|
+
const formatter = useMemo(() => resolveValueFormatter(valueFormatter, format ?? "number", locale), [valueFormatter, format, locale]);
|
|
8406
|
+
const colorFormatter = useMemo(() => resolveValueFormatter(void 0, colorFormat ?? format ?? "number", locale), [colorFormat, format, locale]);
|
|
8407
|
+
const model = useMemo(() => buildTreemapModel({ data: rows, index, group, value, color }), [rows, index, group, value, color]);
|
|
8408
|
+
const colorMode = color ? "numeric" : "category";
|
|
8409
|
+
const colors = useMemo(() => colorsFor4(tokens, tone), [tokens, tone]);
|
|
8410
|
+
const option = useMemo(
|
|
8411
|
+
() => buildTreemapOption({
|
|
8412
|
+
model,
|
|
8413
|
+
hasGroup: !!group,
|
|
8414
|
+
colorMode,
|
|
8415
|
+
toneColor: colors.toneColor,
|
|
8416
|
+
categoryColors: colors.categories,
|
|
8417
|
+
surfaceColor: surface ?? void 0,
|
|
8418
|
+
foregroundColor: colors.foreground || tokens?.foreground || "#111111",
|
|
8419
|
+
labels,
|
|
8420
|
+
valueFormatter: formatter,
|
|
8421
|
+
colorFormatter,
|
|
8422
|
+
colorKey: color,
|
|
8423
|
+
showTooltip,
|
|
8424
|
+
chartOptions,
|
|
8425
|
+
locale
|
|
8426
|
+
}),
|
|
8427
|
+
[model, group, colorMode, colors, surface, tokens, labels, formatter, colorFormatter, color, showTooltip, chartOptions, locale]
|
|
8428
|
+
);
|
|
8429
|
+
const onEvents = useMemo(
|
|
8430
|
+
() => onSelect ? {
|
|
8431
|
+
click: (params) => {
|
|
8432
|
+
const leaf = params.data?.leaf;
|
|
8433
|
+
if (leaf) onSelect(leaf.row);
|
|
8434
|
+
}
|
|
8435
|
+
} : void 0,
|
|
8436
|
+
[onSelect]
|
|
8437
|
+
);
|
|
8438
|
+
const dataTable = useMemo(
|
|
8439
|
+
() => treemapDataTable(model, { index, group, value, color }, { value: formatter, color: colorFormatter }, labels, locale),
|
|
8440
|
+
[model, index, group, value, color, formatter, colorFormatter, labels, locale]
|
|
8441
|
+
);
|
|
8442
|
+
const resolvedSummary = useMemo(
|
|
8443
|
+
() => summary === "auto" ? buildTreemapSummary(model, title, labels, locale) : summary,
|
|
8444
|
+
[summary, model, title, labels, locale]
|
|
8445
|
+
);
|
|
8446
|
+
const label2 = ariaLabel ?? textOf9(locale).chart(labels?.[value] ?? value ?? "");
|
|
8447
|
+
const legendCategories = model.categories.length > MAX_CATEGORIES2 ? [...model.categories.slice(0, MAX_CATEGORIES2), otherSeriesLabel(locale)] : model.categories;
|
|
8448
|
+
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: [
|
|
8449
|
+
color && /* @__PURE__ */ jsx("span", { children: labels?.[color] ?? color }),
|
|
8450
|
+
/* @__PURE__ */ jsx("span", { children: colorFormatter(model.shadeMin) }),
|
|
8451
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "h-2 w-24 rounded-sm", style: { backgroundImage: rampGradientCss(tone) } }),
|
|
8452
|
+
/* @__PURE__ */ jsx("span", { children: colorFormatter(model.shadeMax) })
|
|
8453
|
+
] }) }) : group ? /* @__PURE__ */ jsx(Legend, { categories: legendCategories, labels }) : null;
|
|
8454
|
+
return /* @__PURE__ */ jsx(
|
|
8455
|
+
ChartFrame,
|
|
8456
|
+
{
|
|
8457
|
+
...chartRootProps(rest),
|
|
8458
|
+
ref: frameRef,
|
|
8459
|
+
title,
|
|
8460
|
+
summary: resolvedSummary,
|
|
8461
|
+
isLoading: isLoading ?? false,
|
|
8462
|
+
isEmpty: rows.length === 0,
|
|
8463
|
+
emptyState,
|
|
8464
|
+
height,
|
|
8465
|
+
variant: "bar",
|
|
8466
|
+
ariaLabel: label2,
|
|
8467
|
+
className,
|
|
8468
|
+
dataTable,
|
|
8469
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-full w-full flex-col gap-2", children: [
|
|
8470
|
+
/* @__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 } }) }),
|
|
8471
|
+
legend
|
|
8472
|
+
] })
|
|
8473
|
+
}
|
|
8474
|
+
);
|
|
8475
|
+
}
|
|
8476
|
+
var Treemap = forwardRef(TreemapImpl);
|
|
8477
|
+
Treemap.displayName = "Treemap";
|
|
8478
|
+
var TEXT23 = {
|
|
8479
|
+
ja: {
|
|
8480
|
+
tones: { positive: "\u826F\u597D", warning: "\u6CE8\u610F", negative: "\u8981\u5BFE\u5FDC", neutral: "\u4E2D\u7ACB" },
|
|
8481
|
+
toneNote: (label2) => `\uFF08${label2}\uFF09`,
|
|
8482
|
+
selectAll: "\u5168\u3066\u9078\u629E",
|
|
8483
|
+
selectRow: "\u884C\u3092\u9078\u629E",
|
|
8484
|
+
trend: (points, first, last, min, max) => `${points}\u70B9\u306E\u63A8\u79FB\u3001${first}\u304B\u3089${last}\u3001\u6700\u5C0F${min}\u3001\u6700\u5927${max}`,
|
|
8485
|
+
noData: "\u30C7\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093",
|
|
8486
|
+
search: "\u691C\u7D22",
|
|
8487
|
+
pageSizeLabel: "1\u30DA\u30FC\u30B8\u306E\u8868\u793A\u4EF6\u6570",
|
|
8488
|
+
pageSize: (size) => `${size}\u4EF6`,
|
|
8489
|
+
range: (total, start, end) => `${total}\u4EF6\u4E2D ${start} - ${end} \u3092\u8868\u793A`,
|
|
8490
|
+
first: "\u6700\u521D",
|
|
8491
|
+
previous: "\u524D\u3078",
|
|
8492
|
+
next: "\u6B21\u3078",
|
|
8493
|
+
last: "\u6700\u5F8C",
|
|
8494
|
+
columns: "\u8868\u793A\u3059\u308B\u5217",
|
|
8495
|
+
exportCsv: "CSV \u3092\u66F8\u304D\u51FA\u3059"
|
|
8496
|
+
},
|
|
8497
|
+
en: {
|
|
8498
|
+
tones: { positive: "Good", warning: "Caution", negative: "Needs action", neutral: "Neutral" },
|
|
8499
|
+
toneNote: (label2) => ` (${label2})`,
|
|
8500
|
+
selectAll: "Select all",
|
|
8501
|
+
selectRow: "Select row",
|
|
8502
|
+
trend: (points, first, last, min, max) => `Trend over ${points} points: ${first} to ${last}, min ${min}, max ${max}`,
|
|
8503
|
+
noData: "No results.",
|
|
8504
|
+
search: "Search",
|
|
8505
|
+
pageSizeLabel: "Rows per page",
|
|
8506
|
+
pageSize: (size) => `${size} / page`,
|
|
8507
|
+
range: (total, start, end) => `Showing ${start}\u2013${end} of ${total}`,
|
|
8508
|
+
first: "First",
|
|
8509
|
+
previous: "Previous",
|
|
8510
|
+
next: "Next",
|
|
8511
|
+
last: "Last",
|
|
8512
|
+
columns: "Columns",
|
|
8513
|
+
exportCsv: "Export CSV"
|
|
8514
|
+
}
|
|
8515
|
+
};
|
|
8516
|
+
var useText = () => TEXT23[uiLanguage(useLocale())];
|
|
8517
|
+
function resolveFormat(format, row) {
|
|
8518
|
+
return typeof format === "function" ? format(row) : format;
|
|
8519
|
+
}
|
|
8520
|
+
function isMissingCell(value) {
|
|
8521
|
+
return value === null || value === void 0 || value === "" || typeof value === "number" && Number.isNaN(value);
|
|
8522
|
+
}
|
|
8523
|
+
function formatCellText(value, row, meta, locale) {
|
|
8524
|
+
if (isMissingCell(value)) return meta.nullLabel ?? EMPTY_VALUE;
|
|
8525
|
+
if (meta.dateFormat && (value instanceof Date || typeof value === "string" || typeof value === "number")) {
|
|
8526
|
+
const text = formatDate(value, meta.dateFormat, locale, { timeZone: meta.timeZone });
|
|
8527
|
+
return text === EMPTY_VALUE ? String(value) : text;
|
|
8528
|
+
}
|
|
8529
|
+
if (typeof value !== "number") return String(value);
|
|
8530
|
+
return formatValue(value, resolveFormat(meta.format, row), locale);
|
|
8531
|
+
}
|
|
8532
|
+
var THRESHOLD_TONE_STYLES = {
|
|
8533
|
+
positive: { Icon: CircleCheck, text: "text-positive" },
|
|
8534
|
+
warning: { Icon: TriangleAlert, text: "text-warning" },
|
|
8535
|
+
negative: { Icon: CircleX, text: "text-negative" },
|
|
8536
|
+
neutral: { Icon: Minus, text: "text-neutral" }
|
|
8537
|
+
};
|
|
8538
|
+
function ThresholdCell({ value, text, thresholds }) {
|
|
8539
|
+
const t = useText();
|
|
8540
|
+
const tone = typeof value === "number" ? toneForValue(value, thresholds) : void 0;
|
|
8541
|
+
if (!tone) return /* @__PURE__ */ jsx("div", { className: "text-right tabular-nums", children: text });
|
|
8542
|
+
const { Icon, text: toneClass } = THRESHOLD_TONE_STYLES[tone];
|
|
8543
|
+
const label2 = t.tones[tone];
|
|
8544
|
+
return /* @__PURE__ */ jsxs(
|
|
8545
|
+
"div",
|
|
8546
|
+
{
|
|
8547
|
+
"data-slot": "data-table-threshold-cell",
|
|
8548
|
+
"data-tone": tone,
|
|
8549
|
+
className: cn("flex items-center justify-end gap-1.5 tabular-nums", toneClass),
|
|
8550
|
+
children: [
|
|
8551
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
|
|
8552
|
+
/* @__PURE__ */ jsx("span", { children: text }),
|
|
8553
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t.toneNote(label2) })
|
|
8554
|
+
]
|
|
8555
|
+
}
|
|
8556
|
+
);
|
|
8557
|
+
}
|
|
8558
|
+
function useControlledState(controlledValue, defaultValue, onChange) {
|
|
8559
|
+
const [internalValue, setInternalValue] = useState(defaultValue);
|
|
8560
|
+
const isControlled = controlledValue !== void 0;
|
|
8561
|
+
const value = isControlled ? controlledValue : internalValue;
|
|
8562
|
+
const setValue = useCallback(
|
|
8563
|
+
(updaterOrValue) => {
|
|
8564
|
+
onChange?.(updaterOrValue);
|
|
8565
|
+
if (!isControlled) {
|
|
8566
|
+
setInternalValue(updaterOrValue);
|
|
8567
|
+
}
|
|
8568
|
+
},
|
|
8569
|
+
[onChange, isControlled]
|
|
8570
|
+
);
|
|
8571
|
+
return [value, setValue];
|
|
8572
|
+
}
|
|
8573
|
+
var DataTableContext = createContext(null);
|
|
8574
|
+
function useDataTable() {
|
|
8575
|
+
const ctx = useContext(DataTableContext);
|
|
8576
|
+
if (!ctx) throw new Error("useDataTable must be used inside <DataTable>");
|
|
8577
|
+
return ctx;
|
|
8578
|
+
}
|
|
8579
|
+
var NO_ROWS18 = [];
|
|
8580
|
+
function DataTable({
|
|
8581
|
+
columns = NO_ROWS18,
|
|
8582
|
+
data = NO_ROWS18,
|
|
8583
|
+
enableSorting = true,
|
|
8584
|
+
sorting: sortingProp,
|
|
8585
|
+
onSortingChange,
|
|
8586
|
+
enableFiltering = false,
|
|
8587
|
+
columnFilters: columnFiltersProp,
|
|
8588
|
+
onColumnFiltersChange,
|
|
8589
|
+
globalFilter: globalFilterProp,
|
|
8590
|
+
onGlobalFilterChange,
|
|
8591
|
+
enablePagination = false,
|
|
8592
|
+
pagination: paginationProp,
|
|
8593
|
+
onPaginationChange,
|
|
8594
|
+
pageCount,
|
|
5463
8595
|
rowCount,
|
|
5464
8596
|
manualFiltering,
|
|
5465
8597
|
manualSorting,
|
|
@@ -5474,7 +8606,7 @@ function DataTable({
|
|
|
5474
8606
|
children
|
|
5475
8607
|
}) {
|
|
5476
8608
|
const locale = useLocale();
|
|
5477
|
-
const t =
|
|
8609
|
+
const t = TEXT23[uiLanguage(locale)];
|
|
5478
8610
|
const [sorting, setSorting] = useControlledState(sortingProp, [], onSortingChange);
|
|
5479
8611
|
const [columnFilters, setColumnFilters] = useControlledState(
|
|
5480
8612
|
columnFiltersProp,
|
|
@@ -5633,6 +8765,7 @@ DataTable.displayName = "DataTable";
|
|
|
5633
8765
|
var DataTableToolbar = forwardRef(
|
|
5634
8766
|
({ showSearch, toolbar, bulkActions, className, ...props }, ref) => {
|
|
5635
8767
|
const { globalFilter, setGlobalFilter, hasSelection, selectedRows, enableFiltering } = useDataTable();
|
|
8768
|
+
const t = useText();
|
|
5636
8769
|
const showSearchEffective = showSearch ?? enableFiltering;
|
|
5637
8770
|
const hasContent = showSearchEffective || toolbar != null || hasSelection && bulkActions != null;
|
|
5638
8771
|
if (!hasContent) return null;
|
|
@@ -5649,6 +8782,7 @@ var DataTableToolbar = forwardRef(
|
|
|
5649
8782
|
/* @__PURE__ */ jsx(
|
|
5650
8783
|
Input,
|
|
5651
8784
|
{
|
|
8785
|
+
"aria-label": t.search,
|
|
5652
8786
|
value: globalFilter,
|
|
5653
8787
|
onChange: (e) => setGlobalFilter(e.target.value),
|
|
5654
8788
|
className: "max-w-sm pl-8"
|
|
@@ -5758,19 +8892,19 @@ var DataTableContent = forwardRef(
|
|
|
5758
8892
|
}) }, headerGroup.id)) }),
|
|
5759
8893
|
/* @__PURE__ */ jsx(TableBody, { children: isLoading ? Array.from({ length: enablePagination ? table.getState().pagination.pageSize : 5 }).map(
|
|
5760
8894
|
(_, 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(
|
|
8895
|
+
) : table.getRowModel().rows.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, { "data-state": row.getIsSelected() ? "selected" : void 0, children: row.getVisibleCells().map((cell2) => {
|
|
8896
|
+
const meta = cell2.column.columnDef.meta;
|
|
8897
|
+
const comparisonColumn = comparisonOf.get(cell2.column.id);
|
|
5764
8898
|
const comparisonCell = comparisonColumn ? row.getVisibleCells().find((c) => c.column.id === comparisonColumn.id) : void 0;
|
|
5765
8899
|
return /* @__PURE__ */ jsxs(
|
|
5766
8900
|
TableCell,
|
|
5767
8901
|
{
|
|
5768
8902
|
className: columnClassName(meta, {
|
|
5769
|
-
isComparison: comparisonIds.has(
|
|
5770
|
-
isHost: comparisonOf.has(
|
|
8903
|
+
isComparison: comparisonIds.has(cell2.column.id),
|
|
8904
|
+
isHost: comparisonOf.has(cell2.column.id)
|
|
5771
8905
|
}),
|
|
5772
8906
|
children: [
|
|
5773
|
-
flexRender(
|
|
8907
|
+
flexRender(cell2.column.columnDef.cell, cell2.getContext()),
|
|
5774
8908
|
comparisonCell && /* @__PURE__ */ jsx(
|
|
5775
8909
|
"div",
|
|
5776
8910
|
{
|
|
@@ -5785,7 +8919,7 @@ var DataTableContent = forwardRef(
|
|
|
5785
8919
|
)
|
|
5786
8920
|
]
|
|
5787
8921
|
},
|
|
5788
|
-
|
|
8922
|
+
cell2.id
|
|
5789
8923
|
);
|
|
5790
8924
|
}) }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
|
|
5791
8925
|
TableCell,
|
|
@@ -5832,7 +8966,7 @@ var DataTablePagination = forwardRef(
|
|
|
5832
8966
|
value: String(currentPageSize),
|
|
5833
8967
|
onValueChange: (value) => table.setPageSize(Number(value)),
|
|
5834
8968
|
children: [
|
|
5835
|
-
/* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "min-w-[80px]", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
8969
|
+
/* @__PURE__ */ jsx(SelectTrigger, { size: "sm", className: "min-w-[80px]", "aria-label": t.pageSizeLabel, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
5836
8970
|
/* @__PURE__ */ jsx(SelectContent, { children: pageSizeItems.map((item) => /* @__PURE__ */ jsx(SelectItem, { value: item.value, children: item.label }, item.value)) })
|
|
5837
8971
|
]
|
|
5838
8972
|
}
|
|
@@ -5920,16 +9054,16 @@ DataTableColumnVisibility.displayName = "DataTableColumnVisibility";
|
|
|
5920
9054
|
var SIGNED_NUMBER = /^[+-]\s?[^\d\s=+\-@]{0,3}\d[\d,.]*[^\d\s=+\-@]{0,4}$/;
|
|
5921
9055
|
function toCsv(header, rows, options = {}) {
|
|
5922
9056
|
const { bom = true, guardFormulas = false } = options;
|
|
5923
|
-
const field = (
|
|
5924
|
-
let text =
|
|
5925
|
-
if (guard && typeof
|
|
9057
|
+
const field = (cell2, guard) => {
|
|
9058
|
+
let text = cell2 === null || cell2 === void 0 ? "" : String(cell2);
|
|
9059
|
+
if (guard && typeof cell2 === "string" && /^[=+\-@]/.test(text) && !SIGNED_NUMBER.test(text)) {
|
|
5926
9060
|
text = `'${text}`;
|
|
5927
9061
|
}
|
|
5928
9062
|
return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
|
|
5929
9063
|
};
|
|
5930
9064
|
const lines = [
|
|
5931
9065
|
header.map((h) => field(h, false)).join(","),
|
|
5932
|
-
...rows.map((row) => row.map((
|
|
9066
|
+
...rows.map((row) => row.map((cell2) => field(cell2, guardFormulas)).join(","))
|
|
5933
9067
|
];
|
|
5934
9068
|
return (bom ? "\uFEFF" : "") + lines.join("\r\n");
|
|
5935
9069
|
}
|
|
@@ -5983,7 +9117,7 @@ function DataTableExport({
|
|
|
5983
9117
|
disabled: isLoading,
|
|
5984
9118
|
children: [
|
|
5985
9119
|
/* @__PURE__ */ jsx(Download, { className: "size-4", "aria-hidden": "true" }),
|
|
5986
|
-
children ??
|
|
9120
|
+
children ?? TEXT23[uiLanguage(locale)].exportCsv
|
|
5987
9121
|
]
|
|
5988
9122
|
}
|
|
5989
9123
|
);
|
|
@@ -6017,10 +9151,153 @@ function DataTablePreset({
|
|
|
6017
9151
|
] });
|
|
6018
9152
|
}
|
|
6019
9153
|
DataTablePreset.displayName = "DataTablePreset";
|
|
9154
|
+
var FLOOR_SHARE = 0.2;
|
|
9155
|
+
var DEFAULT_CHART_HEIGHT3 = 288;
|
|
9156
|
+
var LIGHT_TEXT_FROM = 0.55;
|
|
9157
|
+
var dimText = (v) => v === null || v === void 0 || v === "" ? EMPTY_VALUE : String(v);
|
|
9158
|
+
var pivotCellKey = (rowKey, colKey) => `${rowKey.join("")}\0${colKey.join("")}`;
|
|
9159
|
+
function buildPivotModel(config) {
|
|
9160
|
+
const { data, rows, columns, values } = config;
|
|
9161
|
+
const rowKeys = /* @__PURE__ */ new Map();
|
|
9162
|
+
const colKeys = /* @__PURE__ */ new Map();
|
|
9163
|
+
const cells = /* @__PURE__ */ new Map();
|
|
9164
|
+
for (const row of data) {
|
|
9165
|
+
if (!row || typeof row !== "object") continue;
|
|
9166
|
+
const rk = rows.map((d) => dimText(row[d]));
|
|
9167
|
+
const ck = columns.map((d) => dimText(row[d]));
|
|
9168
|
+
const rId = rk.join("");
|
|
9169
|
+
const cId = ck.join("");
|
|
9170
|
+
if (!rowKeys.has(rId)) rowKeys.set(rId, rk);
|
|
9171
|
+
if (!colKeys.has(cId)) colKeys.set(cId, ck);
|
|
9172
|
+
cells.set(pivotCellKey(rk, ck), row);
|
|
9173
|
+
}
|
|
9174
|
+
const ranges = {};
|
|
9175
|
+
for (const value of values) {
|
|
9176
|
+
let [lo, hi] = [Infinity, -Infinity];
|
|
9177
|
+
for (const row of cells.values()) {
|
|
9178
|
+
const n = toChartNumber(row[value]);
|
|
9179
|
+
if (n !== null) [lo, hi] = [Math.min(lo, n), Math.max(hi, n)];
|
|
9180
|
+
}
|
|
9181
|
+
ranges[value] = { min: Number.isFinite(lo) ? lo : 0, max: Number.isFinite(hi) ? hi : 0 };
|
|
9182
|
+
}
|
|
9183
|
+
return {
|
|
9184
|
+
rowKeys: rowKeys.size > 0 ? [...rowKeys.values()] : [rows.map(() => EMPTY_VALUE)],
|
|
9185
|
+
colKeys: colKeys.size > 0 ? [...colKeys.values()] : [columns.map(() => EMPTY_VALUE)],
|
|
9186
|
+
cells,
|
|
9187
|
+
ranges
|
|
9188
|
+
};
|
|
9189
|
+
}
|
|
9190
|
+
function pivotSpans(keys, level) {
|
|
9191
|
+
const out = [];
|
|
9192
|
+
const prefix = (k) => k.slice(0, level + 1).join("");
|
|
9193
|
+
keys.forEach((key, i) => {
|
|
9194
|
+
const last = out[out.length - 1];
|
|
9195
|
+
if (last && prefix(keys[last.start]) === prefix(key)) last.span++;
|
|
9196
|
+
else out.push({ text: key[level] ?? "", start: i, span: 1 });
|
|
9197
|
+
});
|
|
9198
|
+
return out;
|
|
9199
|
+
}
|
|
9200
|
+
var TEXT24 = {
|
|
9201
|
+
ja: { table: "\u30D4\u30DC\u30C3\u30C8\u8868" },
|
|
9202
|
+
en: { table: "Pivot table" }
|
|
9203
|
+
};
|
|
9204
|
+
var NO_ROWS19 = [];
|
|
9205
|
+
var asList = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
9206
|
+
function PivotTable({
|
|
9207
|
+
data = NO_ROWS19,
|
|
9208
|
+
rows: rowDims,
|
|
9209
|
+
columns: colDims,
|
|
9210
|
+
values: valueKeys,
|
|
9211
|
+
formats,
|
|
9212
|
+
labels,
|
|
9213
|
+
highlight = false,
|
|
9214
|
+
tone = "chart-1",
|
|
9215
|
+
onSelect,
|
|
9216
|
+
title,
|
|
9217
|
+
summary,
|
|
9218
|
+
isLoading,
|
|
9219
|
+
emptyState,
|
|
9220
|
+
maxHeight = 480,
|
|
9221
|
+
className,
|
|
9222
|
+
ariaLabel
|
|
9223
|
+
}) {
|
|
9224
|
+
const locale = useLocale();
|
|
9225
|
+
const t = TEXT24[uiLanguage(resolveLocale(locale))];
|
|
9226
|
+
const dataRows = Array.isArray(data) ? data : NO_ROWS19;
|
|
9227
|
+
const rows = useMemo(() => asList(rowDims), [rowDims]);
|
|
9228
|
+
const columns = useMemo(() => asList(colDims), [colDims]);
|
|
9229
|
+
const values = useMemo(() => asList(valueKeys), [valueKeys]);
|
|
9230
|
+
const model = useMemo(() => buildPivotModel({ data: dataRows, rows, columns, values }), [dataRows, rows, columns, values]);
|
|
9231
|
+
const label2 = (key) => labels?.[key] ?? key;
|
|
9232
|
+
const R = rows.length;
|
|
9233
|
+
const C = columns.length;
|
|
9234
|
+
const V = values.length;
|
|
9235
|
+
const valueRow = V > 1 || C === 0;
|
|
9236
|
+
const H = C + (valueRow ? 1 : 0);
|
|
9237
|
+
const shaded = (value) => highlight === true || highlight === value;
|
|
9238
|
+
const isEmpty = dataRows.length === 0;
|
|
9239
|
+
const ready = !isLoading && !isEmpty;
|
|
9240
|
+
const cellStyle = (value, n) => {
|
|
9241
|
+
if (n === null || !shaded(value)) return void 0;
|
|
9242
|
+
const { min, max } = model.ranges[value] ?? { min: 0, max: 0 };
|
|
9243
|
+
const ratio = max > min ? Math.min(1, Math.max(0, (n - min) / (max - min))) : 1;
|
|
9244
|
+
const share = Math.round((FLOOR_SHARE + (1 - FLOOR_SHARE) * ratio) * 100);
|
|
9245
|
+
return {
|
|
9246
|
+
backgroundColor: `color-mix(in oklch, var(--${tone}) ${share}%, var(--card))`,
|
|
9247
|
+
color: ratio >= LIGHT_TEXT_FROM ? "var(--card)" : void 0
|
|
9248
|
+
};
|
|
9249
|
+
};
|
|
9250
|
+
const headerRows = Array.from({ length: H }, (_, h) => {
|
|
9251
|
+
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")] : [];
|
|
9252
|
+
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(
|
|
9253
|
+
(_2, ci) => values.map((v) => /* @__PURE__ */ jsx(TableHead, { scope: "col", className: "text-right", children: label2(v) }, `v-${ci}-${v}`))
|
|
9254
|
+
);
|
|
9255
|
+
return /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-transparent", children: [
|
|
9256
|
+
lead,
|
|
9257
|
+
cells
|
|
9258
|
+
] }, `h-${h}`);
|
|
9259
|
+
});
|
|
9260
|
+
const rowSpans = rows.map((_, level) => new Map(pivotSpans(model.rowKeys, level).map((s) => [s.start, s.span])));
|
|
9261
|
+
const body = model.rowKeys.map((rk, ri) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
9262
|
+
rows.map((d, level) => {
|
|
9263
|
+
const span = rowSpans[level].get(ri);
|
|
9264
|
+
if (span === void 0) return null;
|
|
9265
|
+
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}`);
|
|
9266
|
+
}),
|
|
9267
|
+
model.colKeys.flatMap(
|
|
9268
|
+
(ck, ci) => values.map((v) => {
|
|
9269
|
+
const row = model.cells.get(pivotCellKey(rk, ck));
|
|
9270
|
+
const n = row ? toChartNumber(row[v]) : null;
|
|
9271
|
+
const text = formatValue(n, formats?.[v] ?? "number", locale);
|
|
9272
|
+
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}`);
|
|
9273
|
+
})
|
|
9274
|
+
)
|
|
9275
|
+
] }, `r-${ri}`));
|
|
9276
|
+
return /* @__PURE__ */ jsx(
|
|
9277
|
+
ChartFrame,
|
|
9278
|
+
{
|
|
9279
|
+
title,
|
|
9280
|
+
summary,
|
|
9281
|
+
isLoading: isLoading ?? false,
|
|
9282
|
+
isEmpty,
|
|
9283
|
+
emptyState,
|
|
9284
|
+
height: ready ? "auto" : DEFAULT_CHART_HEIGHT3,
|
|
9285
|
+
variant: "list",
|
|
9286
|
+
plotRole: "group",
|
|
9287
|
+
ariaLabel: ariaLabel ?? title ?? t.table,
|
|
9288
|
+
className,
|
|
9289
|
+
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: [
|
|
9290
|
+
/* @__PURE__ */ jsx(TableHeader, { className: "sticky top-0 z-10 bg-card", children: headerRows }),
|
|
9291
|
+
/* @__PURE__ */ jsx(TableBody, { children: body })
|
|
9292
|
+
] }) })
|
|
9293
|
+
}
|
|
9294
|
+
);
|
|
9295
|
+
}
|
|
6020
9296
|
var FilterBarContext = createContext(null);
|
|
6021
|
-
var
|
|
9297
|
+
var TEXT25 = {
|
|
6022
9298
|
ja: {
|
|
6023
9299
|
filters: "\u30D5\u30A3\u30EB\u30BF\u30FC",
|
|
9300
|
+
addFilter: "\u30D5\u30A3\u30EB\u30BF\u30FC\u3092\u8FFD\u52A0",
|
|
6024
9301
|
selected: (count) => ` (${count}\u4EF6\u9078\u629E\u4E2D)`,
|
|
6025
9302
|
remove: (filter, option) => `${filter}: ${option} \u3092\u524A\u9664`,
|
|
6026
9303
|
clear: "\u30AF\u30EA\u30A2",
|
|
@@ -6028,13 +9305,14 @@ var TEXT15 = {
|
|
|
6028
9305
|
},
|
|
6029
9306
|
en: {
|
|
6030
9307
|
filters: "Filters",
|
|
9308
|
+
addFilter: "Add filter",
|
|
6031
9309
|
selected: (count) => ` (${count} selected)`,
|
|
6032
9310
|
remove: (filter, option) => `Remove ${filter}: ${option}`,
|
|
6033
9311
|
clear: "Clear",
|
|
6034
9312
|
clearAll: "Clear all filters"
|
|
6035
9313
|
}
|
|
6036
9314
|
};
|
|
6037
|
-
var useText2 = () =>
|
|
9315
|
+
var useText2 = () => TEXT25[uiLanguage(useLocale())];
|
|
6038
9316
|
function useFilterBar() {
|
|
6039
9317
|
const ctx = useContext(FilterBarContext);
|
|
6040
9318
|
if (!ctx) throw new Error("useFilterBar must be used within <FilterBar>");
|
|
@@ -6138,6 +9416,50 @@ function FilterBarMultiSelect({
|
|
|
6138
9416
|
] });
|
|
6139
9417
|
}
|
|
6140
9418
|
FilterBarMultiSelect.displayName = "FilterBarMultiSelect";
|
|
9419
|
+
function FilterBarAddFilter({ filters, className }) {
|
|
9420
|
+
const { value, onChange } = useFilterBar();
|
|
9421
|
+
const t = useText2();
|
|
9422
|
+
const toggle = (filter, option) => {
|
|
9423
|
+
if (filter.type === "multi-select") {
|
|
9424
|
+
const current = value[filter.key] ?? [];
|
|
9425
|
+
const next = current.includes(option) ? current.filter((v) => v !== option) : [...current, option];
|
|
9426
|
+
onChange({ ...value, [filter.key]: next.length > 0 ? next : void 0 });
|
|
9427
|
+
} else {
|
|
9428
|
+
onChange({ ...value, [filter.key]: value[filter.key] === option ? void 0 : option });
|
|
9429
|
+
}
|
|
9430
|
+
};
|
|
9431
|
+
const isChecked = (filter, option) => {
|
|
9432
|
+
const v = value[filter.key];
|
|
9433
|
+
return Array.isArray(v) ? v.includes(option) : v === option;
|
|
9434
|
+
};
|
|
9435
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
9436
|
+
/* @__PURE__ */ jsxs(
|
|
9437
|
+
DropdownMenuTrigger,
|
|
9438
|
+
{
|
|
9439
|
+
render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: cn("h-8 gap-1", className) }),
|
|
9440
|
+
"data-slot": "filter-bar-add-filter",
|
|
9441
|
+
children: [
|
|
9442
|
+
/* @__PURE__ */ jsx(Plus, { className: "size-3.5", "aria-hidden": "true" }),
|
|
9443
|
+
t.addFilter
|
|
9444
|
+
]
|
|
9445
|
+
}
|
|
9446
|
+
),
|
|
9447
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: "w-48", children: filters.filter((f) => (f.type === "select" || f.type === "multi-select") && f.options?.length).map((filter) => /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
|
|
9448
|
+
/* @__PURE__ */ jsx(DropdownMenuSubTrigger, { children: filter.label }),
|
|
9449
|
+
/* @__PURE__ */ jsx(DropdownMenuSubContent, { children: filter.options?.map((option) => /* @__PURE__ */ jsx(
|
|
9450
|
+
DropdownMenuCheckboxItem,
|
|
9451
|
+
{
|
|
9452
|
+
checked: isChecked(filter, option.value),
|
|
9453
|
+
disabled: option.disabled,
|
|
9454
|
+
onCheckedChange: () => toggle(filter, option.value),
|
|
9455
|
+
children: option.label
|
|
9456
|
+
},
|
|
9457
|
+
option.value
|
|
9458
|
+
)) })
|
|
9459
|
+
] }, filter.key)) })
|
|
9460
|
+
] });
|
|
9461
|
+
}
|
|
9462
|
+
FilterBarAddFilter.displayName = "FilterBarAddFilter";
|
|
6141
9463
|
function FilterBarActiveChips({ filters, className }) {
|
|
6142
9464
|
const { value, onChange } = useFilterBar();
|
|
6143
9465
|
const t = useText2();
|
|
@@ -6301,6 +9623,11 @@ function fiscalPeriodRange(date, {
|
|
|
6301
9623
|
const periodStart = addMonths(startOfMonth(subMonths(date, into)), offset * length);
|
|
6302
9624
|
return { from: periodStart, to: endOfMonth(addMonths(periodStart, length - 1)) };
|
|
6303
9625
|
}
|
|
9626
|
+
function comparisonRange({ from, to }, kind) {
|
|
9627
|
+
if (kind === "previous-year") return { from: subYears(from, 1), to: subYears(to, 1) };
|
|
9628
|
+
const days = differenceInCalendarDays(to, from) + 1;
|
|
9629
|
+
return { from: subDays(from, days), to: subDays(from, 1) };
|
|
9630
|
+
}
|
|
6304
9631
|
var PRESET_LABELS = {
|
|
6305
9632
|
ja: {
|
|
6306
9633
|
today: "\u4ECA\u65E5",
|
|
@@ -6337,9 +9664,15 @@ var PRESET_LABELS = {
|
|
|
6337
9664
|
lastFiscalYear: "Last fiscal year"
|
|
6338
9665
|
}
|
|
6339
9666
|
};
|
|
6340
|
-
var
|
|
6341
|
-
ja: {
|
|
6342
|
-
|
|
9667
|
+
var TEXT26 = {
|
|
9668
|
+
ja: {
|
|
9669
|
+
placeholder: "\u671F\u9593\u3092\u9078\u629E",
|
|
9670
|
+
comparison: { "previous-period": "\u524D\u671F\u9593", "previous-year": "\u524D\u5E74\u540C\u671F" }
|
|
9671
|
+
},
|
|
9672
|
+
en: {
|
|
9673
|
+
placeholder: "Pick a date range",
|
|
9674
|
+
comparison: { "previous-period": "vs. previous period", "previous-year": "vs. same period last year" }
|
|
9675
|
+
}
|
|
6343
9676
|
};
|
|
6344
9677
|
var calendarLocale = (locale) => uiLanguage(locale) === "ja" ? ja : void 0;
|
|
6345
9678
|
function createDatePresets({
|
|
@@ -6355,7 +9688,7 @@ function createDatePresets({
|
|
|
6355
9688
|
const today = now();
|
|
6356
9689
|
return { from: startOfDay(subDays(today, days - 1)), to: endOfDay(today) };
|
|
6357
9690
|
};
|
|
6358
|
-
const
|
|
9691
|
+
const toDate3 = (unit) => () => {
|
|
6359
9692
|
const today = now();
|
|
6360
9693
|
return {
|
|
6361
9694
|
from: fiscalPeriodRange(today, { unit, fiscalYearStartMonth: startMonth }).from,
|
|
@@ -6405,9 +9738,9 @@ function createDatePresets({
|
|
|
6405
9738
|
return { from: startOfMonth(lastMonth), to: endOfMonth(lastMonth) };
|
|
6406
9739
|
}
|
|
6407
9740
|
},
|
|
6408
|
-
{ label: t.thisQuarter, getValue:
|
|
9741
|
+
{ label: t.thisQuarter, getValue: toDate3("quarter") },
|
|
6409
9742
|
{ label: t.lastQuarter, getValue: previous("quarter") },
|
|
6410
|
-
{ label: t.thisFiscalYear, getValue:
|
|
9743
|
+
{ label: t.thisFiscalYear, getValue: toDate3("year") },
|
|
6411
9744
|
{ label: t.lastFiscalYear, getValue: previous("year") }
|
|
6412
9745
|
];
|
|
6413
9746
|
}
|
|
@@ -6428,6 +9761,7 @@ var DateRangePicker = forwardRef(
|
|
|
6428
9761
|
showPresets = true,
|
|
6429
9762
|
locale: localeProp,
|
|
6430
9763
|
fiscalYearStartMonth,
|
|
9764
|
+
comparison,
|
|
6431
9765
|
maxDate,
|
|
6432
9766
|
minDate,
|
|
6433
9767
|
placeholder,
|
|
@@ -6438,7 +9772,7 @@ var DateRangePicker = forwardRef(
|
|
|
6438
9772
|
const [open, setOpen] = useState(false);
|
|
6439
9773
|
const isMobile = useIsMobile();
|
|
6440
9774
|
const locale = useLocale(localeProp);
|
|
6441
|
-
const t =
|
|
9775
|
+
const t = TEXT26[uiLanguage(locale)];
|
|
6442
9776
|
const presets = useMemo(
|
|
6443
9777
|
() => presetsProp ?? createDatePresets({ locale, fiscalYearStartMonth }),
|
|
6444
9778
|
[presetsProp, locale, fiscalYearStartMonth]
|
|
@@ -6459,10 +9793,14 @@ var DateRangePicker = forwardRef(
|
|
|
6459
9793
|
const formatDateRange2 = () => {
|
|
6460
9794
|
if (!value?.from) return placeholder ?? t.placeholder;
|
|
6461
9795
|
if (!value.to) return formatDate(value.from, "date", locale);
|
|
6462
|
-
return
|
|
9796
|
+
return formatDateRange(value.from, value.to, { locale });
|
|
6463
9797
|
};
|
|
6464
9798
|
const selectedPreset = findMatchingPreset(presets, value);
|
|
6465
9799
|
const presetLabel = selectedPreset?.label;
|
|
9800
|
+
const comparisonText = comparison && value?.from && value.to ? (() => {
|
|
9801
|
+
const c = comparisonRange({ from: value.from, to: value.to }, comparison);
|
|
9802
|
+
return `${t.comparison[comparison]} ${formatDateRange(c.from, c.to, { locale })}`;
|
|
9803
|
+
})() : void 0;
|
|
6466
9804
|
const endMonth = value?.to ?? value?.from;
|
|
6467
9805
|
const defaultMonth = endMonth ? isMobile ? endMonth : subMonths(endMonth, 1) : void 0;
|
|
6468
9806
|
const calendarSelected = value?.from ? { from: value.from, to: value.to ?? void 0 } : void 0;
|
|
@@ -6489,7 +9827,8 @@ var DateRangePicker = forwardRef(
|
|
|
6489
9827
|
presetLabel ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6490
9828
|
/* @__PURE__ */ jsx("span", { children: presetLabel }),
|
|
6491
9829
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: formatDateRange2() })
|
|
6492
|
-
] }) : /* @__PURE__ */ jsx("span", { children: formatDateRange2() })
|
|
9830
|
+
] }) : /* @__PURE__ */ jsx("span", { children: formatDateRange2() }),
|
|
9831
|
+
comparisonText && /* @__PURE__ */ jsx("span", { "data-slot": "date-range-picker-comparison", className: "text-muted-foreground", children: comparisonText })
|
|
6493
9832
|
]
|
|
6494
9833
|
}
|
|
6495
9834
|
),
|
|
@@ -6655,7 +9994,7 @@ function RemoveButton({ label: label2, onRemove, iconClassName }) {
|
|
|
6655
9994
|
}
|
|
6656
9995
|
);
|
|
6657
9996
|
}
|
|
6658
|
-
var
|
|
9997
|
+
var TEXT27 = {
|
|
6659
9998
|
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
9999
|
en: { placeholder: "Select an option", search: "Search...", empty: "No results found.", clear: "Clear" }
|
|
6661
10000
|
};
|
|
@@ -6675,7 +10014,7 @@ var SearchableSelect = forwardRef(
|
|
|
6675
10014
|
className
|
|
6676
10015
|
}, ref) {
|
|
6677
10016
|
const [open, setOpen] = useState(false);
|
|
6678
|
-
const t =
|
|
10017
|
+
const t = TEXT27[uiLanguage(useLocale())];
|
|
6679
10018
|
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
6680
10019
|
const selectedOption = useMemo(
|
|
6681
10020
|
() => allOptions.find((opt) => opt.value === value),
|
|
@@ -6728,7 +10067,7 @@ var SearchableSelect = forwardRef(
|
|
|
6728
10067
|
}
|
|
6729
10068
|
);
|
|
6730
10069
|
SearchableSelect.displayName = "SearchableSelect";
|
|
6731
|
-
var
|
|
10070
|
+
var TEXT28 = {
|
|
6732
10071
|
ja: {
|
|
6733
10072
|
placeholder: "\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
6734
10073
|
search: "\u691C\u7D22...",
|
|
@@ -6745,10 +10084,10 @@ var TEXT18 = {
|
|
|
6745
10084
|
}
|
|
6746
10085
|
};
|
|
6747
10086
|
var NO_OPTIONS3 = [];
|
|
6748
|
-
var
|
|
10087
|
+
var NO_VALUES2 = [];
|
|
6749
10088
|
var MultiSelect = forwardRef(function MultiSelect2({
|
|
6750
10089
|
options = NO_OPTIONS3,
|
|
6751
|
-
value =
|
|
10090
|
+
value = NO_VALUES2,
|
|
6752
10091
|
onChange,
|
|
6753
10092
|
onSearch,
|
|
6754
10093
|
maxCount = 3,
|
|
@@ -6761,7 +10100,7 @@ var MultiSelect = forwardRef(function MultiSelect2({
|
|
|
6761
10100
|
}, ref) {
|
|
6762
10101
|
const [open, setOpen] = useState(false);
|
|
6763
10102
|
const listId = useId();
|
|
6764
|
-
const t =
|
|
10103
|
+
const t = TEXT28[uiLanguage(useLocale())];
|
|
6765
10104
|
const allOptions = useMemo(() => flattenOptions(options), [options]);
|
|
6766
10105
|
const selectedOptions = useMemo(
|
|
6767
10106
|
() => allOptions.filter((opt) => value.includes(opt.value)),
|
|
@@ -6891,10 +10230,10 @@ function computeTargetProgress(current, target, options) {
|
|
|
6891
10230
|
elapsed
|
|
6892
10231
|
};
|
|
6893
10232
|
}
|
|
6894
|
-
var
|
|
10233
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
6895
10234
|
function inferPeriodGrain(period) {
|
|
6896
10235
|
if (!period) return void 0;
|
|
6897
|
-
const days = (toTime(period.end) - toTime(period.start)) /
|
|
10236
|
+
const days = (toTime(period.end) - toTime(period.start)) / DAY_MS2;
|
|
6898
10237
|
if (!Number.isFinite(days)) return void 0;
|
|
6899
10238
|
const near = (min, max) => days >= min - 1 / 24 && days <= max + 1 / 24;
|
|
6900
10239
|
if (near(1, 1)) return "day";
|
|
@@ -6936,7 +10275,7 @@ var deltaVariants = cva(
|
|
|
6936
10275
|
{
|
|
6937
10276
|
variants: {
|
|
6938
10277
|
variant: {
|
|
6939
|
-
badge: "h-5 rounded-md
|
|
10278
|
+
badge: "h-5 rounded-md px-2 py-0.5 text-xs font-medium [&>svg]:size-3",
|
|
6940
10279
|
inline: "text-sm [&>svg]:size-3.5"
|
|
6941
10280
|
},
|
|
6942
10281
|
sentiment: {
|
|
@@ -6946,9 +10285,9 @@ var deltaVariants = cva(
|
|
|
6946
10285
|
}
|
|
6947
10286
|
},
|
|
6948
10287
|
compoundVariants: [
|
|
6949
|
-
{ variant: "badge", sentiment: "positive", className: "
|
|
6950
|
-
{ variant: "badge", sentiment: "negative", className: "
|
|
6951
|
-
{ variant: "badge", sentiment: "neutral", className: "
|
|
10288
|
+
{ variant: "badge", sentiment: "positive", className: "bg-positive-subtle" },
|
|
10289
|
+
{ variant: "badge", sentiment: "negative", className: "bg-negative-subtle" },
|
|
10290
|
+
{ variant: "badge", sentiment: "neutral", className: "bg-neutral-subtle" }
|
|
6952
10291
|
],
|
|
6953
10292
|
defaultVariants: {
|
|
6954
10293
|
variant: "badge",
|
|
@@ -6981,6 +10320,7 @@ function deltaAriaLabel(result, locale, change = "relative", format, digits) {
|
|
|
6981
10320
|
if (amount === null) return t.none(label2);
|
|
6982
10321
|
return t.change(label2, change === "points" ? amount.replace(/pt$/, t.points) : amount, direction === "up");
|
|
6983
10322
|
}
|
|
10323
|
+
var SENTIMENT_TEXT = { positive: "text-positive", negative: "text-negative", neutral: "text-neutral" };
|
|
6984
10324
|
var Delta = forwardRef(
|
|
6985
10325
|
({
|
|
6986
10326
|
value,
|
|
@@ -7010,7 +10350,7 @@ var Delta = forwardRef(
|
|
|
7010
10350
|
const comparable = result.direction !== "none";
|
|
7011
10351
|
const Icon = result.direction === "up" ? ArrowUp : result.direction === "down" ? ArrowDown : ArrowRight;
|
|
7012
10352
|
const text = formatChange(result, display, locale, format, changeDigits);
|
|
7013
|
-
return /* @__PURE__ */
|
|
10353
|
+
return /* @__PURE__ */ jsx(
|
|
7014
10354
|
"span",
|
|
7015
10355
|
{
|
|
7016
10356
|
ref,
|
|
@@ -7020,16 +10360,31 @@ var Delta = forwardRef(
|
|
|
7020
10360
|
"data-sentiment": result.sentiment,
|
|
7021
10361
|
"aria-label": deltaAriaLabel(result, locale, display, format, changeDigits),
|
|
7022
10362
|
className: cn(
|
|
7023
|
-
deltaVariants({ variant, sentiment: result.sentiment }),
|
|
10363
|
+
variant === "badge" ? "inline-flex w-fit shrink-0 items-center gap-1.5 font-medium whitespace-nowrap tabular-nums" : deltaVariants({ variant, sentiment: result.sentiment }),
|
|
10364
|
+
// The chip carries the colour of the change; the root repeats it for the "—" case and for `className` consumers.
|
|
10365
|
+
variant === "badge" && SENTIMENT_TEXT[result.sentiment],
|
|
7024
10366
|
!comparable && "text-muted-foreground",
|
|
7025
10367
|
className
|
|
7026
10368
|
),
|
|
7027
10369
|
...props,
|
|
7028
|
-
children: [
|
|
10370
|
+
children: variant === "badge" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10371
|
+
/* @__PURE__ */ jsxs(
|
|
10372
|
+
"span",
|
|
10373
|
+
{
|
|
10374
|
+
"data-slot": "delta-chip",
|
|
10375
|
+
className: cn(deltaVariants({ variant, sentiment: result.sentiment }), !comparable && "text-muted-foreground"),
|
|
10376
|
+
children: [
|
|
10377
|
+
comparable && /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }),
|
|
10378
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: text ?? DELTA_NOT_COMPARABLE })
|
|
10379
|
+
]
|
|
10380
|
+
}
|
|
10381
|
+
),
|
|
10382
|
+
showLabel && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-xs font-normal text-muted-foreground", children: result.label })
|
|
10383
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7029
10384
|
comparable && /* @__PURE__ */ jsx(Icon, { "aria-hidden": "true" }),
|
|
7030
10385
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: text ?? DELTA_NOT_COMPARABLE }),
|
|
7031
10386
|
showLabel && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "font-normal text-muted-foreground", children: result.label })
|
|
7032
|
-
]
|
|
10387
|
+
] })
|
|
7033
10388
|
}
|
|
7034
10389
|
);
|
|
7035
10390
|
}
|
|
@@ -7043,7 +10398,7 @@ var LONG_VALUE_CHARS = 10;
|
|
|
7043
10398
|
var DELTA_UNDER_PLACEMENT = "col-start-1 row-span-1 row-start-3 justify-self-start";
|
|
7044
10399
|
var KPI_FOOTER_CLASS = "flex-col items-start gap-2 rounded-none border-t-0 bg-transparent pt-0 text-sm";
|
|
7045
10400
|
var PLACEHOLDER = /\{(\w+)\}/g;
|
|
7046
|
-
var
|
|
10401
|
+
var TEXT29 = {
|
|
7047
10402
|
ja: {
|
|
7048
10403
|
target: "\u76EE\u6A19",
|
|
7049
10404
|
provisional: "\u901F\u5831",
|
|
@@ -7175,7 +10530,7 @@ function KpiCard({
|
|
|
7175
10530
|
...props
|
|
7176
10531
|
}) {
|
|
7177
10532
|
const locale = useLocale(localeProp);
|
|
7178
|
-
const t =
|
|
10533
|
+
const t = TEXT29[uiLanguage(locale)];
|
|
7179
10534
|
const allComparisons = [...comparisonProp ? [comparisonProp] : [], ...comparisons ?? []];
|
|
7180
10535
|
const comparison = allComparisons[0];
|
|
7181
10536
|
const isProvisional = provisional !== void 0 && provisional !== false;
|
|
@@ -7205,7 +10560,7 @@ function KpiCard({
|
|
|
7205
10560
|
const targetBar = hasTarget && !neutral && !byPoints && !lower;
|
|
7206
10561
|
if (summary !== void 0) warnShallowSummary(summary, t);
|
|
7207
10562
|
const cardClass = cn("row-span-2 grid content-start gap-y-0 grid-rows-subgrid", className);
|
|
7208
|
-
const valueSize = small ? "text-xl @[218px]/card:text-2xl" : "text-2xl @[218px]/card:text-
|
|
10563
|
+
const valueSize = small ? "text-xl @[218px]/card:text-2xl" : "text-2xl @[218px]/card:text-[26px]";
|
|
7209
10564
|
if (isLoading) {
|
|
7210
10565
|
return /* @__PURE__ */ jsxs(
|
|
7211
10566
|
Card,
|
|
@@ -7310,7 +10665,8 @@ function KpiCard({
|
|
|
7310
10665
|
format,
|
|
7311
10666
|
change: display,
|
|
7312
10667
|
digits,
|
|
7313
|
-
locale
|
|
10668
|
+
locale,
|
|
10669
|
+
className: "min-w-0 shrink whitespace-normal"
|
|
7314
10670
|
},
|
|
7315
10671
|
i
|
|
7316
10672
|
));
|
|
@@ -7318,16 +10674,20 @@ function KpiCard({
|
|
|
7318
10674
|
const longValue = valueText.length > LONG_VALUE_CHARS;
|
|
7319
10675
|
return /* @__PURE__ */ jsxs(Card, { "data-slot": "kpi-card", size, className: cardClass, ...props, children: [
|
|
7320
10676
|
/* @__PURE__ */ jsxs(CardHeader, { className: HEADER_CLASS, children: [
|
|
7321
|
-
/* @__PURE__ */
|
|
10677
|
+
/* @__PURE__ */ jsxs(CardDescription, { "data-slot": "kpi-card-label", title: label2, className: "col-start-1 flex min-w-0 items-center gap-2 text-[13px] leading-snug font-medium text-muted-foreground", children: [
|
|
10678
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: label2 }),
|
|
10679
|
+
sample && /* @__PURE__ */ jsx(SampleBadge, { label: sample.label, className: "shrink-0" })
|
|
10680
|
+
] }),
|
|
7322
10681
|
/* @__PURE__ */ jsxs(
|
|
7323
10682
|
CardTitle,
|
|
7324
10683
|
{
|
|
7325
10684
|
"data-slot": "kpi-card-value",
|
|
7326
10685
|
title: valueText,
|
|
7327
10686
|
className: cn(
|
|
7328
|
-
"col-start-1 flex min-w-0
|
|
10687
|
+
"col-start-1 flex min-w-0 items-center gap-x-2 gap-y-1 leading-none font-medium tracking-tight tabular-nums",
|
|
7329
10688
|
// Fixed size: the step follows the card width only, never the length of the value.
|
|
7330
|
-
valueSize
|
|
10689
|
+
valueSize,
|
|
10690
|
+
(longValue || isProvisional) && "flex-wrap"
|
|
7331
10691
|
),
|
|
7332
10692
|
children: [
|
|
7333
10693
|
/* @__PURE__ */ jsx("span", { "data-slot": "kpi-card-value-text", className: longValue ? "min-w-0 break-words" : "whitespace-nowrap", children: longValue ? digitGroups(valueText).map((group, i) => /* @__PURE__ */ jsxs("span", { className: "whitespace-nowrap", children: [
|
|
@@ -7335,11 +10695,11 @@ function KpiCard({
|
|
|
7335
10695
|
/* @__PURE__ */ jsx("wbr", {})
|
|
7336
10696
|
] }, i)) : valueText }),
|
|
7337
10697
|
isProvisional && /* @__PURE__ */ jsx(Badge, { "data-slot": "kpi-card-provisional", variant: "outline", className: "align-middle", children: provisionalLabel }),
|
|
7338
|
-
|
|
10698
|
+
comparison && !stacked && !longValue && deltas
|
|
7339
10699
|
]
|
|
7340
10700
|
}
|
|
7341
10701
|
),
|
|
7342
|
-
comparison && (stacked ? /* @__PURE__ */ jsx("div", { "data-slot": "kpi-card-comparisons", className: "flex flex-wrap items-center gap-1", children: deltas }) : /* @__PURE__ */ jsx(CardAction, { className:
|
|
10702
|
+
comparison && (stacked ? /* @__PURE__ */ jsx("div", { "data-slot": "kpi-card-comparisons", className: "flex flex-wrap items-center gap-1", children: deltas }) : longValue && /* @__PURE__ */ jsx(CardAction, { className: DELTA_UNDER_PLACEMENT, children: deltas })),
|
|
7343
10703
|
hasPrevious && comparison && /* @__PURE__ */ jsx("div", { "data-slot": "kpi-card-previous", className: "col-span-full text-sm text-muted-foreground tabular-nums", children: result?.previous != null ? t.previous(baseline ?? "", fmt(result.previous)) : t.noPrevious(baseline ?? "") }),
|
|
7344
10704
|
bar && progress && /* @__PURE__ */ jsxs("div", { "data-slot": "kpi-card-target-progress", className: "col-span-full mt-1 flex min-w-0 flex-col gap-1.5", children: [
|
|
7345
10705
|
/* @__PURE__ */ jsxs(
|
|
@@ -7412,7 +10772,7 @@ function KpiCard({
|
|
|
7412
10772
|
{
|
|
7413
10773
|
data: historyPoints.map((v) => ({ value: v })),
|
|
7414
10774
|
height: SPARKLINE_HEIGHT,
|
|
7415
|
-
color: "
|
|
10775
|
+
color: "chart-1",
|
|
7416
10776
|
highlight: "latest",
|
|
7417
10777
|
"aria-label": t.history(label2, historyPoints.length)
|
|
7418
10778
|
}
|
|
@@ -7426,7 +10786,7 @@ function updatedAtText(updatedAt, display, locale, t, now, timeZone) {
|
|
|
7426
10786
|
const absolute2 = formatDate(updatedAt, "datetime", locale, { timeZone });
|
|
7427
10787
|
return display === "absolute" ? t.updatedAbsolute(absolute2) : t.updatedBoth(absolute2, relative2);
|
|
7428
10788
|
}
|
|
7429
|
-
var
|
|
10789
|
+
var TEXT30 = {
|
|
7430
10790
|
ja: {
|
|
7431
10791
|
flat: (value) => `\u5909\u5316\u306A\u3057 ${value}%`,
|
|
7432
10792
|
up: (value) => `${value}% \u5897\u52A0`,
|
|
@@ -7455,7 +10815,7 @@ var TrendIndicator = forwardRef(
|
|
|
7455
10815
|
const isGood = direction === "neutral" ? null : direction === "up" === positiveIsGood;
|
|
7456
10816
|
const sentiment = isGood === null ? "neutral" : isGood ? "positive" : "negative";
|
|
7457
10817
|
const Icon = direction === "up" ? ArrowUp : direction === "down" ? ArrowDown : Minus;
|
|
7458
|
-
const t =
|
|
10818
|
+
const t = TEXT30[uiLanguage(useLocale())];
|
|
7459
10819
|
const ariaLabel = direction === "neutral" ? t.flat(value) : direction === "up" ? t.up(value) : t.down(value);
|
|
7460
10820
|
return /* @__PURE__ */ jsxs(
|
|
7461
10821
|
"span",
|
|
@@ -7476,93 +10836,6 @@ var TrendIndicator = forwardRef(
|
|
|
7476
10836
|
}
|
|
7477
10837
|
);
|
|
7478
10838
|
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
10839
|
var Placeholder = forwardRef(function Placeholder2({ className, ...props }, ref) {
|
|
7567
10840
|
return /* @__PURE__ */ jsx(
|
|
7568
10841
|
"span",
|
|
@@ -7575,7 +10848,69 @@ var Placeholder = forwardRef(function Placeholder2({ className, ...props }, ref)
|
|
|
7575
10848
|
);
|
|
7576
10849
|
});
|
|
7577
10850
|
Placeholder.displayName = "Placeholder";
|
|
10851
|
+
function DraftChart({ children }) {
|
|
10852
|
+
if (!import.meta.env.DEV) return null;
|
|
10853
|
+
return /* @__PURE__ */ jsx(DraftChartPreview, { children });
|
|
10854
|
+
}
|
|
10855
|
+
var STYLE_ID = "vantage-draft-chart";
|
|
10856
|
+
var DRAFT_CSS = `@layer components {
|
|
10857
|
+
[data-sq-draft] {
|
|
10858
|
+
position: relative;
|
|
10859
|
+
isolation: isolate;
|
|
10860
|
+
}
|
|
10861
|
+
[data-sq-draft]::after {
|
|
10862
|
+
content: "";
|
|
10863
|
+
position: absolute;
|
|
10864
|
+
inset: -1px;
|
|
10865
|
+
z-index: 2147483647;
|
|
10866
|
+
border-radius: inherit;
|
|
10867
|
+
background-color: var(--background);
|
|
10868
|
+
opacity: 0.75;
|
|
10869
|
+
pointer-events: none;
|
|
10870
|
+
transition: opacity 200ms ease;
|
|
10871
|
+
}
|
|
10872
|
+
[data-sq-draft]:hover::after {
|
|
10873
|
+
opacity: 0;
|
|
10874
|
+
}
|
|
10875
|
+
@media (prefers-reduced-motion: reduce) {
|
|
10876
|
+
[data-sq-draft]::after {
|
|
10877
|
+
transition: none;
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
}`;
|
|
10881
|
+
function DraftChartPreview({ children }) {
|
|
10882
|
+
const ref = useRef(null);
|
|
10883
|
+
useInsertionEffect(() => {
|
|
10884
|
+
let style = document.getElementById(STYLE_ID);
|
|
10885
|
+
if (!style) {
|
|
10886
|
+
style = document.createElement("style");
|
|
10887
|
+
style.id = STYLE_ID;
|
|
10888
|
+
document.head.appendChild(style);
|
|
10889
|
+
}
|
|
10890
|
+
if (style.textContent !== DRAFT_CSS) style.textContent = DRAFT_CSS;
|
|
10891
|
+
}, []);
|
|
10892
|
+
useLayoutEffect(() => {
|
|
10893
|
+
const wrapper = ref.current;
|
|
10894
|
+
if (!wrapper) return;
|
|
10895
|
+
let marked = null;
|
|
10896
|
+
const mark = () => {
|
|
10897
|
+
const root = wrapper.firstElementChild;
|
|
10898
|
+
if (root === marked) return;
|
|
10899
|
+
marked?.removeAttribute("data-sq-draft");
|
|
10900
|
+
root?.setAttribute("data-sq-draft", "");
|
|
10901
|
+
marked = root;
|
|
10902
|
+
};
|
|
10903
|
+
mark();
|
|
10904
|
+
const observer = new MutationObserver(mark);
|
|
10905
|
+
observer.observe(wrapper, { childList: true });
|
|
10906
|
+
return () => {
|
|
10907
|
+
observer.disconnect();
|
|
10908
|
+
marked?.removeAttribute("data-sq-draft");
|
|
10909
|
+
};
|
|
10910
|
+
}, []);
|
|
10911
|
+
return /* @__PURE__ */ jsx("div", { ref, "data-slot": "draft-chart", style: { display: "contents" }, children });
|
|
10912
|
+
}
|
|
7578
10913
|
|
|
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-
|
|
10914
|
+
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, FilterBarAddFilter, 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 };
|
|
10915
|
+
//# sourceMappingURL=chunk-I2GWIPTD.js.map
|
|
10916
|
+
//# sourceMappingURL=chunk-I2GWIPTD.js.map
|