@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.
Files changed (101) hide show
  1. package/README.md +1 -1
  2. package/dist/add-AU3EDPNW.js +6 -0
  3. package/dist/add-AU3EDPNW.js.map +1 -0
  4. package/dist/{chunk-AABMKHEU.js → chunk-35B3YIBF.js} +16 -7
  5. package/dist/chunk-35B3YIBF.js.map +1 -0
  6. package/dist/{chunk-QNOX7WLZ.js → chunk-5HBUBVQ3.js} +94 -8
  7. package/dist/chunk-5HBUBVQ3.js.map +1 -0
  8. package/dist/{chunk-EG4PX726.js → chunk-GWXKENNH.js} +122 -5
  9. package/dist/chunk-GWXKENNH.js.map +1 -0
  10. package/dist/{chunk-77CISBMG.js → chunk-I2GWIPTD.js} +3685 -350
  11. package/dist/chunk-I2GWIPTD.js.map +1 -0
  12. package/dist/{chunk-TRMHNMMZ.js → chunk-LYUGDHSF.js} +3 -3
  13. package/dist/{chunk-TRMHNMMZ.js.map → chunk-LYUGDHSF.js.map} +1 -1
  14. package/dist/{chunk-2IAPIOPS.js → chunk-U6MWBCM6.js} +28 -7
  15. package/dist/chunk-U6MWBCM6.js.map +1 -0
  16. package/dist/{chunk-3RJEOP3J.js → chunk-XRKSIC5Z.js} +5 -3
  17. package/dist/chunk-XRKSIC5Z.js.map +1 -0
  18. package/dist/cli.js +2 -2
  19. package/dist/components/index.d.ts +849 -12
  20. package/dist/components/index.js +5 -5
  21. package/dist/{delta-text-CbrOaW83.d.ts → delta-text-tav2Lh5d.d.ts} +1 -1
  22. package/dist/doc/index.d.ts +2 -2
  23. package/dist/doc/index.js +4 -4
  24. package/dist/{format-DihQkL1N.d.ts → format-D34piCei.d.ts} +2 -1
  25. package/dist/geo-japan-7BMSOJRX.js +8 -0
  26. package/dist/geo-japan-7BMSOJRX.js.map +1 -0
  27. package/dist/geo-us-GVOG7VXH.js +8 -0
  28. package/dist/geo-us-GVOG7VXH.js.map +1 -0
  29. package/dist/geo-world-35HL56UU.js +8 -0
  30. package/dist/geo-world-35HL56UU.js.map +1 -0
  31. package/dist/slide/index.d.ts +2 -2
  32. package/dist/slide/index.js +4 -4
  33. package/dist/table/index.js +5 -6
  34. package/dist/table/index.js.map +1 -1
  35. package/dist/ui/index.d.ts +35 -4
  36. package/dist/ui/index.js +174 -11
  37. package/dist/ui/index.js.map +1 -1
  38. package/dist/{upgrade-6DT7ADRP.js → upgrade-MCHMELWC.js} +3 -3
  39. package/dist/{upgrade-6DT7ADRP.js.map → upgrade-MCHMELWC.js.map} +1 -1
  40. package/docs/en/agent-skills.md +13 -14
  41. package/docs/en/changelog.md +8 -0
  42. package/docs/en/parts/draft-chart.md +42 -0
  43. package/docs/en/parts/page-shell.md +1 -1
  44. package/docs/index.json +70 -50
  45. package/docs/ja/agent-skills.md +14 -14
  46. package/docs/ja/changelog.md +8 -0
  47. package/docs/ja/parts/draft-chart.md +40 -0
  48. package/docs/ja/parts/page-shell.md +1 -1
  49. package/package.json +2 -2
  50. package/registry/parts.json +5319 -0
  51. package/registry/ui/area-chart.tsx +43 -8
  52. package/registry/ui/bar-chart.tsx +43 -8
  53. package/registry/ui/bar-list.tsx +43 -8
  54. package/registry/ui/box-plot-chart.tsx +42 -7
  55. package/registry/ui/category-bar.tsx +25 -5
  56. package/registry/ui/chart-frame.tsx +55 -14
  57. package/registry/ui/checkbox.tsx +4 -3
  58. package/registry/ui/code-block.tsx +44 -0
  59. package/registry/ui/combo-chart.tsx +1869 -0
  60. package/registry/ui/combobox.tsx +7 -0
  61. package/registry/ui/context-menu.tsx +71 -0
  62. package/registry/ui/dashboard-card.tsx +23 -10
  63. package/registry/ui/data-table.tsx +10 -2
  64. package/registry/ui/date-range-picker.tsx +51 -4
  65. package/registry/ui/delta.tsx +39 -12
  66. package/registry/ui/donut-chart.tsx +43 -8
  67. package/registry/ui/echart.tsx +167 -7
  68. package/registry/ui/file-dropzone.tsx +143 -0
  69. package/registry/ui/filter-bar.tsx +77 -2
  70. package/registry/ui/forecast-chart.tsx +4769 -0
  71. package/registry/ui/funnel-steps.tsx +23 -5
  72. package/registry/ui/heatmap.tsx +20 -3
  73. package/registry/ui/histogram-chart.tsx +42 -7
  74. package/registry/ui/kpi-card.tsx +15 -9
  75. package/registry/ui/legend.tsx +73 -15
  76. package/registry/ui/line-chart.tsx +43 -8
  77. package/registry/ui/metric-value.tsx +2 -2
  78. package/registry/ui/page-shell.tsx +27 -4
  79. package/registry/ui/paired-chart.tsx +44 -9
  80. package/registry/ui/pivot-table.tsx +1260 -0
  81. package/registry/ui/progress-circle.tsx +2 -2
  82. package/registry/ui/progress.tsx +1 -1
  83. package/registry/ui/scatter-chart.tsx +50 -12
  84. package/registry/ui/sparkline.tsx +44 -9
  85. package/registry/ui/states.tsx +34 -1
  86. package/registry/ui/status-badge.tsx +7 -7
  87. package/registry/ui/tabs.tsx +1 -0
  88. package/registry/ui/treemap.tsx +1934 -0
  89. package/registry/ui/waterfall-chart.tsx +64 -11
  90. package/templates/AGENTS.md +3 -3
  91. package/templates/agents/page.md +5 -4
  92. package/dist/add-4GHF2TX2.js +0 -6
  93. package/dist/add-4GHF2TX2.js.map +0 -1
  94. package/dist/chunk-2IAPIOPS.js.map +0 -1
  95. package/dist/chunk-3RJEOP3J.js.map +0 -1
  96. package/dist/chunk-77CISBMG.js.map +0 -1
  97. package/dist/chunk-AABMKHEU.js.map +0 -1
  98. package/dist/chunk-EG4PX726.js.map +0 -1
  99. package/dist/chunk-NZ5QIIG4.js +0 -74
  100. package/dist/chunk-NZ5QIIG4.js.map +0 -1
  101. package/dist/chunk-QNOX7WLZ.js.map +0 -1
@@ -1,19 +1,19 @@
1
- import { ChartFrameContext, useChartSurface, resolveChartColors, CHART_OTHER_COLOR, EChart, resolveFillColors } from './chunk-EG4PX726.js';
2
- import { Sparkline } from './chunk-2IAPIOPS.js';
3
- import { formatDateRange, EMPTY_VALUE, formatDate, toValueFormatter, formatValue, formatPoints, formatKind, formatDelta, MINUS_SIGN, formatDatesInText, DEFAULT_LOCALE } from './chunk-3RJEOP3J.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, Checkbox, Card, CardHeader, CardDescription, CardTitle, CardAction, CardFooter } from './chunk-QNOX7WLZ.js';
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, useCallback, Fragment as Fragment$1, useContext } from 'react';
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 px-3 py-1.5 group-data-[size=sm]:px-2", className),
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 hasHeader = title || description || actions || sample;
662
- return /* @__PURE__ */ jsx(DashboardCard, { className, subgrid, ...props, children: /* @__PURE__ */ jsxs(SampleClaim, { children: [
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
- (title || description) && /* @__PURE__ */ jsxs("div", { children: [
665
- title && /* @__PURE__ */ jsx(DashboardCardTitle, { children: title }),
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
- (actions || sample) && /* @__PURE__ */ jsxs(DashboardCardAction, { className: cn(sample && actions && "flex items-center gap-2"), children: [
669
- sample && /* @__PURE__ */ jsx(SampleBadge, { label: sample.label }),
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: true, children })
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
- (cell, c) => c === 0 ? /* @__PURE__ */ jsx("th", { scope: "row", children: cell }, c) : /* @__PURE__ */ jsx("td", { children: cell }, c)
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, percent) {
804
- return `color-mix(in oklab, ${cssColor} ${percent}%, transparent)`;
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, toDate2, locale) {
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" && toDate2) return nouns["prior-year"].unknown;
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, toDate2, locale) {
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, toDate2, resolved));
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 percent = kind !== "line" && type === "percent";
1772
+ const percent2 = kind !== "line" && type === "percent";
1744
1773
  const horizontalBars = kind === "bar" && layout === "vertical";
1745
- const comparison = kind !== "bar" && !percent ? cfg.comparison : void 0;
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 forecast = kind !== "bar" && !percent && cfg.forecast && cfg.forecast.data.length > 0 && data.length > 0 && categories.length > 0 ? cfg.forecast : void 0;
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 (forecast) {
1792
+ if (forecast2) {
1763
1793
  const position = new Map(labels.map((label2, i) => [label2, i]));
1764
- for (const row of forecast.data) {
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 && !percent ? stackTotalsOf(mainIdx.map((i) => values[i])) : null;
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 = percent && kind === "area";
1804
- const picked = highlight !== "auto" || mainIdx.length === 0 || percent && !shareHighlight ? [] : shareHighlight ? categories.length <= 2 ? pickShareHighlightPoints(values[0] ?? []) : [] : stackTotals ? pickHighlightPoints(stackTotals) : mainIdx.length <= 2 ? pickHighlightPoints(values[highlightSeries] ?? []) : [];
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" && !percent && cfg.forecast && cfg.forecast.data.length > 0 ? lastActual : -1;
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 && !percent && (stacked || categories.length === 1) ? barTargetValues(data, targetKey) : null;
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 && c !== categories.length - 1 ? 0 : rounded;
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
- itemStyle: { color, borderRadius: radius },
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: percent && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, color, opacity: dimSeries ? DIMMED : 1 },
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: percent && c === categories.length - 1 ? { width: 0, color, opacity: 0 } : { width: 2, type: "dashed", color, opacity },
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 = forecast?.label ?? text.forecast;
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 (forecast) {
2013
- const category = forecast.category !== void 0 && categories.includes(forecast.category) && forecast.category !== comparison?.category ? forecast.category : categories[mainIdx[0] ?? 0];
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
- forecast.data.forEach((row, k) => {
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 = percent ? 100 : maxValue ?? Math.max(target, ...totals);
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 (forecast) {
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 = percent ? 100 : maxValue ?? Math.max(0, ...(stacked ? stackTotalsOf(values) : values.flat()).map((v) => v ?? 0));
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 = percent ? (v) => `${v}%` : (v) => axisFormatter(v);
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 || percent) return axisFormatter;
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 (forecast) addAll(forecast.data.flatMap((row) => [cellNumber(row.lower), cellNumber(row.upper)]));
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: percent ? 0 : kind === "line" ? minValue === void 0 ? fit ? void 0 : zeroBasedMin : limits.min : limits.min,
2318
- max: percent ? 100 : limits.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: percent ? (v) => `${v}%` : (v) => tickFormatter(v),
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
- ...forecast ? [forecastLabel] : [],
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 } : forecast && name === forecastLabel ? { name, icon: FORECAST_DASHED_ICON } : hasTarget && name === targetText ? { name, icon: TARGET_DOTTED_ICON } : name
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
- (forecast ? FORECAST_LABEL_ROOM : 0),
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 = percent ? cartesianSeriesValues(i <= lastActual ? [row] : [], categories, "percent") : null;
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
- forecast,
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 ?? loading ?? false,
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 ?? loading ?? false,
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
- forecast,
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 ?? loading ?? false,
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 ?? loading ?? false,
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
- return /* @__PURE__ */ jsxs("div", { ref, "data-slot": "category-bar", className: cn("w-full", className), ...props, children: [
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("span", { "aria-label": marker.tooltip, className: "block py-1" }), children: markerDot }),
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 stepLabelLayout(plotWidth, steps) {
3879
- return stepSlotWidth(plotWidth, steps) >= STEP_LABEL_MIN_SLOT ? { width: stepLabelWidth(plotWidth, steps), rotate: 0 } : { rotate: 45 };
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 right edge.
4382
- ...headingWidth > 0 ? { left: headingWidth } : legendBelow ? { left: 0 } : { right: config.reserveActions ? 72 : 0 },
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: 24,
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: 6,
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
- labelLayout: { hideOverlap: true, moveOverlap: "shiftY" }
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) => /* @__PURE__ */ jsxs("li", { "data-slot": "legend-item", className: "flex min-w-0 items-center gap-1.5", children: [
4639
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "size-2.5 shrink-0 rounded-[2px]", style: { background: swatches[i] } }),
4640
- /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: labelOf(category, labels) })
4641
- ] }, category))
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 cell = (plotWidth + HEATMAP_BUCKET_MID - HEATMAP_GRID_X - (widest(rowNames) + 8)) / Math.max(1, columns);
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 <= cell) return 12;
5089
- return text * 10 / 12 + HEATMAP_CELL_PADDING <= cell ? 10 : null;
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 = (cell) => cell.flagged ? { borderColor: config.flagColor, borderWidth: 3, borderType: "solid" } : { borderColor: "transparent", borderWidth: 1 };
5118
- const tooltip = (cell) => {
5119
- const head = `${escapeHtml2(label2(model.ys[cell.yi]))} ${escapeHtml2(label2(model.xs[cell.xi]))}`;
5120
- const main = cell.value === null ? EMPTY_VALUE : escapeHtml2(valueFormatter(cell.value));
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 = cell.row ? toChartNumber(cell.row[key]) : null;
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 = cell.flagged ? `${t.join}${t.flagged}` : "";
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((cell) => cell.value === null ? [] : [valueFormatter(cell.value)])
5202
+ model.cells.flatMap((cell2) => cell2.value === null ? [] : [valueFormatter(cell2.value)])
5133
5203
  ) : 12;
5134
- const point = (cell) => ({ value: [cell.xi, cell.yi, cell.value], cell });
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((cell) => ({
5183
- ...point(cell),
5184
- itemStyle: flagged(cell),
5252
+ data: present.map((cell2) => ({
5253
+ ...point(cell2),
5254
+ itemStyle: flagged(cell2),
5185
5255
  label: {
5186
- color: heatmapRatio(cell.value, model.min, model.max) >= HEATMAP_LIGHT_TEXT_FROM ? config.darkCellText : config.lightCellText
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((cell) => ({ value: [cell.xi, cell.yi, 0], cell, itemStyle: flagged(cell) })),
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 cell of model.cells) if (cell.value !== null && (!best || cell.value > best.value)) best = cell;
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
- tones: { positive: "\u826F\u597D", warning: "\u6CE8\u610F", negative: "\u8981\u5BFE\u5FDC", neutral: "\u4E2D\u7ACB" },
5353
- toneNote: (label2) => `\uFF08${label2}\uFF09`,
5354
- selectAll: "\u5168\u3066\u9078\u629E",
5355
- selectRow: "\u884C\u3092\u9078\u629E",
5356
- trend: (points, first, last, min, max) => `${points}\u70B9\u306E\u63A8\u79FB\u3001${first}\u304B\u3089${last}\u3001\u6700\u5C0F${min}\u3001\u6700\u5927${max}`,
5357
- noData: "\u30C7\u30FC\u30BF\u304C\u3042\u308A\u307E\u305B\u3093",
5358
- pageSize: (size) => `${size}\u4EF6`,
5359
- range: (total, start, end) => `${total}\u4EF6\u4E2D ${start} - ${end} \u3092\u8868\u793A`,
5360
- first: "\u6700\u521D",
5361
- previous: "\u524D\u3078",
5362
- next: "\u6B21\u3078",
5363
- last: "\u6700\u5F8C",
5364
- columns: "\u8868\u793A\u3059\u308B\u5217",
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
- tones: { positive: "Good", warning: "Caution", negative: "Needs action", neutral: "Neutral" },
5369
- toneNote: (label2) => ` (${label2})`,
5370
- selectAll: "Select all",
5371
- selectRow: "Select row",
5372
- trend: (points, first, last, min, max) => `Trend over ${points} points: ${first} to ${last}, min ${min}, max ${max}`,
5373
- noData: "No results.",
5374
- pageSize: (size) => `${size} / page`,
5375
- range: (total, start, end) => `Showing ${start}\u2013${end} of ${total}`,
5376
- first: "First",
5377
- previous: "Previous",
5378
- next: "Next",
5379
- last: "Last",
5380
- columns: "Columns",
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 useText = () => TEXT14[uiLanguage(useLocale())];
5385
- function resolveFormat(format, row) {
5386
- return typeof format === "function" ? format(row) : format;
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 isMissingCell(value) {
5389
- return value === null || value === void 0 || value === "" || typeof value === "number" && Number.isNaN(value);
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 formatCellText(value, row, meta, locale) {
5392
- if (isMissingCell(value)) return meta.nullLabel ?? EMPTY_VALUE;
5393
- if (meta.dateFormat && (value instanceof Date || typeof value === "string" || typeof value === "number")) {
5394
- const text = formatDate(value, meta.dateFormat, locale, { timeZone: meta.timeZone });
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
- var THRESHOLD_TONE_STYLES = {
5401
- positive: { Icon: CircleCheck, text: "text-positive" },
5402
- warning: { Icon: TriangleAlert, text: "text-warning" },
5403
- negative: { Icon: CircleX, text: "text-negative" },
5404
- neutral: { Icon: Minus, text: "text-neutral" }
5405
- };
5406
- function ThresholdCell({ value, text, thresholds }) {
5407
- const t = useText();
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
- /* @__PURE__ */ jsx(Icon, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
5420
- /* @__PURE__ */ jsx("span", { children: text }),
5421
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: t.toneNote(label2) })
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
- function useControlledState(controlledValue, defaultValue, onChange) {
5427
- const [internalValue, setInternalValue] = useState(defaultValue);
5428
- const isControlled = controlledValue !== void 0;
5429
- const value = isControlled ? controlledValue : internalValue;
5430
- const setValue = useCallback(
5431
- (updaterOrValue) => {
5432
- onChange?.(updaterOrValue);
5433
- if (!isControlled) {
5434
- setInternalValue(updaterOrValue);
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
- [onChange, isControlled]
5438
- );
5439
- return [value, setValue];
5440
- }
5441
- var DataTableContext = createContext(null);
5442
- function useDataTable() {
5443
- const ctx = useContext(DataTableContext);
5444
- if (!ctx) throw new Error("useDataTable must be used inside <DataTable>");
5445
- return ctx;
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 NO_ROWS12 = [];
5448
- function DataTable({
5449
- columns = NO_ROWS12,
5450
- data = NO_ROWS12,
5451
- enableSorting = true,
5452
- sorting: sortingProp,
5453
- onSortingChange,
5454
- enableFiltering = false,
5455
- columnFilters: columnFiltersProp,
5456
- onColumnFiltersChange,
5457
- globalFilter: globalFilterProp,
5458
- onGlobalFilterChange,
5459
- enablePagination = false,
5460
- pagination: paginationProp,
5461
- onPaginationChange,
5462
- pageCount,
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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[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 = TEXT14[uiLanguage(locale)];
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((cell) => {
5762
- const meta = cell.column.columnDef.meta;
5763
- const comparisonColumn = comparisonOf.get(cell.column.id);
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(cell.column.id),
5770
- isHost: comparisonOf.has(cell.column.id)
8903
+ isComparison: comparisonIds.has(cell2.column.id),
8904
+ isHost: comparisonOf.has(cell2.column.id)
5771
8905
  }),
5772
8906
  children: [
5773
- flexRender(cell.column.columnDef.cell, cell.getContext()),
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
- cell.id
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 = (cell, guard) => {
5924
- let text = cell === null || cell === void 0 ? "" : String(cell);
5925
- if (guard && typeof cell === "string" && /^[=+\-@]/.test(text) && !SIGNED_NUMBER.test(text)) {
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((cell) => field(cell, guardFormulas)).join(","))
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 ?? TEXT14[uiLanguage(locale)].exportCsv
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 TEXT15 = {
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 = () => TEXT15[uiLanguage(useLocale())];
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 TEXT16 = {
6341
- ja: { placeholder: "\u671F\u9593\u3092\u9078\u629E", rangeSeparator: " - " },
6342
- en: { placeholder: "Pick a date range", rangeSeparator: " \u2013 " }
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 toDate2 = (unit) => () => {
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: toDate2("quarter") },
9741
+ { label: t.thisQuarter, getValue: toDate3("quarter") },
6409
9742
  { label: t.lastQuarter, getValue: previous("quarter") },
6410
- { label: t.thisFiscalYear, getValue: toDate2("year") },
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 = TEXT16[uiLanguage(locale)];
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 `${formatDate(value.from, "date", locale)}${t.rangeSeparator}${formatDate(value.to, "date", locale)}`;
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 TEXT17 = {
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 = TEXT17[uiLanguage(useLocale())];
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 TEXT18 = {
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 NO_VALUES = [];
10087
+ var NO_VALUES2 = [];
6749
10088
  var MultiSelect = forwardRef(function MultiSelect2({
6750
10089
  options = NO_OPTIONS3,
6751
- value = NO_VALUES,
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 = TEXT18[uiLanguage(useLocale())];
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 DAY_MS = 24 * 60 * 60 * 1e3;
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)) / DAY_MS;
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 border px-2 py-0.5 text-xs font-medium [&>svg]:size-3",
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: "border-positive/20 bg-positive-subtle dark:border-positive/25" },
6950
- { variant: "badge", sentiment: "negative", className: "border-negative/20 bg-negative-subtle dark:border-negative/25" },
6951
- { variant: "badge", sentiment: "neutral", className: "border-neutral/20 bg-neutral-subtle dark:border-neutral/25" }
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__ */ jsxs(
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 TEXT19 = {
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 = TEXT19[uiLanguage(locale)];
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-4xl";
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__ */ jsx(CardDescription, { "data-slot": "kpi-card-label", title: label2, className: "col-start-1 min-w-0 truncate text-[13px] leading-snug font-medium text-muted-foreground", children: label2 }),
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 flex-wrap items-center gap-x-2 gap-y-1 leading-none font-semibold tracking-tight tabular-nums",
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
- sample && /* @__PURE__ */ jsx(SampleBadge, { label: sample.label })
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: longValue ? DELTA_UNDER_PLACEMENT : DELTA_PLACEMENT, children: deltas })),
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: "text-chart-1",
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 TEXT20 = {
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 = TEXT20[uiLanguage(useLocale())];
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-77CISBMG.js.map
7581
- //# sourceMappingURL=chunk-77CISBMG.js.map
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