@galaxy-io/dls 1.2.6 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/avatar/Avatar.d.ts +2 -1
- package/dist/avatar/Avatar.js +103 -14
- package/dist/buttons/Button.d.ts +6 -1
- package/dist/buttons/Button.js +8 -4
- package/dist/charts/BarChart.d.ts +30 -20
- package/dist/charts/BarChart.js +216 -250
- package/dist/charts/ChartAxis.d.ts +47 -22
- package/dist/charts/ChartAxis.js +81 -60
- package/dist/charts/ChartEmptyState.d.ts +17 -0
- package/dist/charts/ChartEmptyState.js +29 -0
- package/dist/charts/ChartFrame.d.ts +82 -0
- package/dist/charts/ChartFrame.js +86 -0
- package/dist/charts/ChartLegend.d.ts +27 -4
- package/dist/charts/ChartLegend.js +46 -23
- package/dist/charts/ChartPrimitives.d.ts +195 -0
- package/dist/charts/ChartPrimitives.js +268 -0
- package/dist/charts/ChartTooltip.d.ts +27 -6
- package/dist/charts/ChartTooltip.js +56 -50
- package/dist/charts/LineChart.d.ts +42 -20
- package/dist/charts/LineChart.js +186 -237
- package/dist/charts/PieChart.d.ts +48 -32
- package/dist/charts/PieChart.js +159 -171
- package/dist/charts/barChartGeometry.d.ts +87 -0
- package/dist/charts/barChartGeometry.js +218 -0
- package/dist/charts/chartFormat.d.ts +19 -0
- package/dist/charts/chartFormat.js +59 -0
- package/dist/charts/chartScales.d.ts +84 -0
- package/dist/charts/chartScales.js +111 -0
- package/dist/charts/chartTheme.d.ts +18 -0
- package/dist/charts/chartTheme.js +26 -0
- package/dist/charts/constants.d.ts +225 -18
- package/dist/charts/constants.js +240 -32
- package/dist/charts/lineChartGeometry.d.ts +54 -0
- package/dist/charts/lineChartGeometry.js +143 -0
- package/dist/charts/pieChartGeometry.d.ts +48 -0
- package/dist/charts/pieChartGeometry.js +89 -0
- package/dist/charts/types.d.ts +522 -54
- package/dist/charts/types.js +136 -0
- package/dist/charts/useChartDimensions.d.ts +40 -0
- package/dist/charts/useChartDimensions.js +53 -0
- package/dist/charts/useChartFormatters.d.ts +19 -0
- package/dist/charts/useChartFormatters.js +20 -0
- package/dist/charts/useChartInteraction.d.ts +71 -0
- package/dist/charts/useChartInteraction.js +130 -0
- package/dist/charts/useChartTooltipPosition.d.ts +53 -0
- package/dist/charts/useChartTooltipPosition.js +39 -0
- package/dist/hooks/useElementClientSize.d.ts +20 -0
- package/dist/hooks/useElementClientSize.js +43 -0
- package/dist/inputs/CheckboxInput.d.ts +7 -2
- package/dist/inputs/CheckboxInput.js +30 -14
- package/dist/inputs/Input.d.ts +5 -2
- package/dist/inputs/Input.js +20 -17
- package/dist/inputs/MultiSelectInput.js +1 -1
- package/dist/inputs/RadioInput.js +7 -2
- package/dist/styles.css +68 -75
- package/dist/table/InfiniteSpreadsheet.d.ts +6 -0
- package/dist/table/InfiniteSpreadsheet.js +235 -0
- package/dist/table/InfiniteTable.d.ts +7 -88
- package/dist/table/InfiniteTable.js +366 -577
- package/dist/{spreadsheet → table}/SpreadsheetCell.d.ts +6 -4
- package/dist/table/SpreadsheetCell.js +71 -0
- package/dist/{spreadsheet → table}/SpreadsheetCellEditor.js +27 -21
- package/dist/table/SpreadsheetHeaderCell.d.ts +23 -0
- package/dist/table/SpreadsheetHeaderCell.js +91 -0
- package/dist/table/SpreadsheetMenuItem.d.ts +14 -0
- package/dist/table/SpreadsheetMenuItem.js +33 -0
- package/dist/table/SpreadsheetPrimitives.d.ts +172 -0
- package/dist/table/SpreadsheetPrimitives.js +105 -0
- package/dist/table/SpreadsheetRow.d.ts +33 -0
- package/dist/table/SpreadsheetRow.js +73 -0
- package/dist/{spreadsheet → table}/SpreadsheetRowActionCell.d.ts +5 -7
- package/dist/table/SpreadsheetRowActionCell.js +51 -0
- package/dist/table/TableExpandCell.d.ts +11 -0
- package/dist/table/TableExpandCell.js +24 -0
- package/dist/table/TableExpandedRow.d.ts +16 -0
- package/dist/table/TableExpandedRow.js +77 -0
- package/dist/table/TableHeaderCellContent.d.ts +30 -0
- package/dist/table/TableHeaderCellContent.js +48 -0
- package/dist/table/TablePrimitives.d.ts +307 -0
- package/dist/table/TablePrimitives.js +298 -0
- package/dist/table/TableSelectCell.d.ts +18 -0
- package/dist/table/TableSelectCell.js +26 -0
- package/dist/table/TableStatusRow.d.ts +18 -0
- package/dist/table/TableStatusRow.js +50 -0
- package/dist/table/VirtualizedInfiniteTable.d.ts +11 -27
- package/dist/table/VirtualizedInfiniteTable.js +182 -317
- package/dist/table/types.d.ts +249 -0
- package/dist/table/types.js +51 -0
- package/dist/table/useInfiniteScrollSentinel.d.ts +24 -0
- package/dist/table/useInfiniteScrollSentinel.js +53 -0
- package/dist/{spreadsheet/hooks → table}/useSpreadsheetEditing.d.ts +1 -1
- package/dist/{spreadsheet/hooks → table}/useSpreadsheetEditing.js +1 -1
- package/dist/table/useTableColumnLayout.d.ts +28 -0
- package/dist/table/useTableColumnLayout.js +63 -0
- package/dist/table/useTableColumnResize.d.ts +38 -0
- package/dist/table/useTableColumnResize.js +155 -0
- package/dist/table/useTableKeyboardNavigation.d.ts +25 -0
- package/dist/table/useTableKeyboardNavigation.js +83 -0
- package/dist/table/useTableRowExpansion.d.ts +24 -0
- package/dist/table/useTableRowExpansion.js +62 -0
- package/dist/table/useTableRowSelection.d.ts +27 -0
- package/dist/table/useTableRowSelection.js +94 -0
- package/dist/table/utils.d.ts +50 -0
- package/dist/table/utils.js +78 -0
- package/dist/text/MarkdownText.js +45 -27
- package/dist/theme/variants/dark.js +1 -1
- package/dist/widget/Widget.d.ts +2 -0
- package/dist/widget/Widget.js +14 -7
- package/package.json +1 -5
- package/dist/assets/components/enrichment/PerplexityIcon.d.ts +0 -3
- package/dist/assets/components/enrichment/PerplexityIcon.js +0 -35
- package/dist/assets/components/enrichment/TavilyIcon.d.ts +0 -3
- package/dist/assets/components/enrichment/TavilyIcon.js +0 -15
- package/dist/assets/components/onboarding/TenantSetupDark.d.ts +0 -3
- package/dist/assets/components/onboarding/TenantSetupDark.js +0 -498
- package/dist/assets/components/onboarding/TenantSetupLight.d.ts +0 -3
- package/dist/assets/components/onboarding/TenantSetupLight.js +0 -482
- package/dist/assets/components/onboarding/UserSetupDark.d.ts +0 -3
- package/dist/assets/components/onboarding/UserSetupDark.js +0 -1086
- package/dist/assets/components/onboarding/UserSetupLight.d.ts +0 -3
- package/dist/assets/components/onboarding/UserSetupLight.js +0 -1024
- package/dist/assets/components/onboarding/WorkspaceSetupBuildDark.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupBuildDark.js +0 -466
- package/dist/assets/components/onboarding/WorkspaceSetupBuildLight.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupBuildLight.js +0 -460
- package/dist/assets/components/onboarding/WorkspaceSetupConnectDark.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupConnectDark.js +0 -336
- package/dist/assets/components/onboarding/WorkspaceSetupConnectLight.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupConnectLight.js +0 -336
- package/dist/assets/components/onboarding/WorkspaceSetupIntegrateDark.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupIntegrateDark.js +0 -153
- package/dist/assets/components/onboarding/WorkspaceSetupIntegrateLight.d.ts +0 -3
- package/dist/assets/components/onboarding/WorkspaceSetupIntegrateLight.js +0 -153
- package/dist/assets/components/projects/MappingMethodAutoIntrospectDark.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodAutoIntrospectDark.js +0 -117
- package/dist/assets/components/projects/MappingMethodAutoIntrospectLight.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodAutoIntrospectLight.js +0 -117
- package/dist/assets/components/projects/MappingMethodBlankDark.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodBlankDark.js +0 -150
- package/dist/assets/components/projects/MappingMethodBlankDarkHovered.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodBlankDarkHovered.js +0 -150
- package/dist/assets/components/projects/MappingMethodBlankLight.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodBlankLight.js +0 -150
- package/dist/assets/components/projects/MappingMethodBlankLightHovered.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodBlankLightHovered.js +0 -150
- package/dist/assets/components/projects/MappingMethodJsonDark.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodJsonDark.js +0 -90
- package/dist/assets/components/projects/MappingMethodJsonDarkHovered.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodJsonDarkHovered.js +0 -90
- package/dist/assets/components/projects/MappingMethodJsonLight.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodJsonLight.js +0 -90
- package/dist/assets/components/projects/MappingMethodJsonLightHovered.d.ts +0 -3
- package/dist/assets/components/projects/MappingMethodJsonLightHovered.js +0 -90
- package/dist/assets/components/projects/ProjectsEmptyStateDark.d.ts +0 -3
- package/dist/assets/components/projects/ProjectsEmptyStateDark.js +0 -454
- package/dist/assets/components/projects/ProjectsEmptyStateLight.d.ts +0 -3
- package/dist/assets/components/projects/ProjectsEmptyStateLight.js +0 -456
- package/dist/assets/components/root/CreateProjectActionCardDark.d.ts +0 -3
- package/dist/assets/components/root/CreateProjectActionCardDark.js +0 -1796
- package/dist/assets/components/root/CreateProjectActionCardDarkHovered.d.ts +0 -3
- package/dist/assets/components/root/CreateProjectActionCardDarkHovered.js +0 -1796
- package/dist/assets/components/root/CreateProjectActionCardLight.d.ts +0 -3
- package/dist/assets/components/root/CreateProjectActionCardLight.js +0 -1786
- package/dist/assets/components/root/CreateProjectActionCardLightHovered.d.ts +0 -3
- package/dist/assets/components/root/CreateProjectActionCardLightHovered.js +0 -1786
- package/dist/assets/components/root/CreateSourceActionCardDark.d.ts +0 -3
- package/dist/assets/components/root/CreateSourceActionCardDark.js +0 -879
- package/dist/assets/components/root/CreateSourceActionCardDarkHovered.d.ts +0 -3
- package/dist/assets/components/root/CreateSourceActionCardDarkHovered.js +0 -879
- package/dist/assets/components/root/CreateSourceActionCardLight.d.ts +0 -3
- package/dist/assets/components/root/CreateSourceActionCardLight.js +0 -889
- package/dist/assets/components/root/CreateSourceActionCardLightHovered.d.ts +0 -3
- package/dist/assets/components/root/CreateSourceActionCardLightHovered.js +0 -889
- package/dist/assets/components/sources/AttioIcon.d.ts +0 -3
- package/dist/assets/components/sources/AttioIcon.js +0 -11
- package/dist/assets/components/sources/BigquerySourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/BigquerySourceIcon.js +0 -39
- package/dist/assets/components/sources/BrowserbaseSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/BrowserbaseSourceIcon.js +0 -35
- package/dist/assets/components/sources/ClickhouseLogoSourceIconDark.d.ts +0 -3
- package/dist/assets/components/sources/ClickhouseLogoSourceIconDark.js +0 -13
- package/dist/assets/components/sources/ClickhouseLogoSourceIconLight.d.ts +0 -3
- package/dist/assets/components/sources/ClickhouseLogoSourceIconLight.js +0 -13
- package/dist/assets/components/sources/ConfluenceSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/ConfluenceSourceIcon.js +0 -30
- package/dist/assets/components/sources/GithubSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/GithubSourceIcon.js +0 -27
- package/dist/assets/components/sources/GoogleDriveIcon.d.ts +0 -3
- package/dist/assets/components/sources/GoogleDriveIcon.js +0 -45
- package/dist/assets/components/sources/HubspotSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/HubspotSourceIcon.js +0 -14
- package/dist/assets/components/sources/JiraSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/JiraSourceIcon.js +0 -54
- package/dist/assets/components/sources/LinearSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/LinearSourceIcon.js +0 -14
- package/dist/assets/components/sources/MysqlLogoSourceIconDark.d.ts +0 -3
- package/dist/assets/components/sources/MysqlLogoSourceIconDark.js +0 -17
- package/dist/assets/components/sources/NetsuiteSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/NetsuiteSourceIcon.js +0 -32
- package/dist/assets/components/sources/NotionIcon.d.ts +0 -3
- package/dist/assets/components/sources/NotionIcon.js +0 -20
- package/dist/assets/components/sources/PostgresqlIcon.d.ts +0 -3
- package/dist/assets/components/sources/PostgresqlIcon.js +0 -61
- package/dist/assets/components/sources/S3Icon.d.ts +0 -3
- package/dist/assets/components/sources/S3Icon.js +0 -76
- package/dist/assets/components/sources/SalesforceSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/SalesforceSourceIcon.js +0 -23
- package/dist/assets/components/sources/SlackIcon.d.ts +0 -3
- package/dist/assets/components/sources/SlackIcon.js +0 -32
- package/dist/assets/components/sources/SnowflakeLogoSourceIconDark.d.ts +0 -3
- package/dist/assets/components/sources/SnowflakeLogoSourceIconDark.js +0 -14
- package/dist/assets/components/sources/SnowflakeLogoSourceIconLight.d.ts +0 -3
- package/dist/assets/components/sources/SnowflakeLogoSourceIconLight.js +0 -14
- package/dist/assets/components/sources/WikipediaSourceIcon.d.ts +0 -3
- package/dist/assets/components/sources/WikipediaSourceIcon.js +0 -1490
- package/dist/avatar/AvatarEmpty.d.ts +0 -7
- package/dist/avatar/AvatarEmpty.js +0 -99
- package/dist/avatar/types.d.ts +0 -5
- package/dist/avatar/types.js +0 -9
- package/dist/charts/utils.d.ts +0 -29
- package/dist/charts/utils.js +0 -62
- package/dist/icons/BackIcon.d.ts +0 -3
- package/dist/icons/BackIcon.js +0 -13
- package/dist/icons/CloseIcon.d.ts +0 -3
- package/dist/icons/CloseIcon.js +0 -13
- package/dist/icons/CreateIcon.d.ts +0 -3
- package/dist/icons/CreateIcon.js +0 -13
- package/dist/icons/ForwardIcon.d.ts +0 -3
- package/dist/icons/ForwardIcon.js +0 -13
- package/dist/icons/HelpIcon.d.ts +0 -3
- package/dist/icons/HelpIcon.js +0 -14
- package/dist/icons/ProjectsIcon.d.ts +0 -3
- package/dist/icons/ProjectsIcon.js +0 -12
- package/dist/icons/SearchIcon.d.ts +0 -3
- package/dist/icons/SearchIcon.js +0 -12
- package/dist/icons/SourcesIcon.d.ts +0 -3
- package/dist/icons/SourcesIcon.js +0 -12
- package/dist/icons/TextIcon.d.ts +0 -4
- package/dist/icons/TextIcon.js +0 -22
- package/dist/icons/WebScrapeIcon.d.ts +0 -4
- package/dist/icons/WebScrapeIcon.js +0 -22
- package/dist/icons/WebhookIcon.d.ts +0 -4
- package/dist/icons/WebhookIcon.js +0 -22
- package/dist/icons/sources/AttioLogomark.d.ts +0 -4
- package/dist/icons/sources/AttioLogomark.js +0 -8
- package/dist/icons/sources/BrowserbaseLogomark.d.ts +0 -4
- package/dist/icons/sources/BrowserbaseLogomark.js +0 -8
- package/dist/icons/sources/ClickhouseLogomark.d.ts +0 -4
- package/dist/icons/sources/ClickhouseLogomark.js +0 -16
- package/dist/icons/sources/ConfluenceLogomark.d.ts +0 -4
- package/dist/icons/sources/ConfluenceLogomark.js +0 -8
- package/dist/icons/sources/GithubLogomark.d.ts +0 -4
- package/dist/icons/sources/GithubLogomark.js +0 -8
- package/dist/icons/sources/GoogleBigqueryLogomark.d.ts +0 -4
- package/dist/icons/sources/GoogleBigqueryLogomark.js +0 -8
- package/dist/icons/sources/GoogleDriveLogomark.d.ts +0 -4
- package/dist/icons/sources/GoogleDriveLogomark.js +0 -8
- package/dist/icons/sources/HubspotLogomark.d.ts +0 -4
- package/dist/icons/sources/HubspotLogomark.js +0 -8
- package/dist/icons/sources/JiraLogomark.d.ts +0 -4
- package/dist/icons/sources/JiraLogomark.js +0 -8
- package/dist/icons/sources/LinearLogomark.d.ts +0 -4
- package/dist/icons/sources/LinearLogomark.js +0 -8
- package/dist/icons/sources/MySQLLogomark.d.ts +0 -4
- package/dist/icons/sources/MySQLLogomark.js +0 -8
- package/dist/icons/sources/NetsuiteLogomark.d.ts +0 -4
- package/dist/icons/sources/NetsuiteLogomark.js +0 -8
- package/dist/icons/sources/NotionLogomark.d.ts +0 -4
- package/dist/icons/sources/NotionLogomark.js +0 -8
- package/dist/icons/sources/PostgresLogomark.d.ts +0 -4
- package/dist/icons/sources/PostgresLogomark.js +0 -8
- package/dist/icons/sources/S3Logomark.d.ts +0 -4
- package/dist/icons/sources/S3Logomark.js +0 -8
- package/dist/icons/sources/SalesforceLogomark.d.ts +0 -4
- package/dist/icons/sources/SalesforceLogomark.js +0 -8
- package/dist/icons/sources/SlackLogoIconmark.d.ts +0 -4
- package/dist/icons/sources/SlackLogoIconmark.js +0 -8
- package/dist/icons/sources/SnowflakeLogomark.d.ts +0 -4
- package/dist/icons/sources/SnowflakeLogomark.js +0 -16
- package/dist/icons/sources/WikipediaLogomark.d.ts +0 -4
- package/dist/icons/sources/WikipediaLogomark.js +0 -8
- package/dist/spreadsheet/InfiniteSpreadsheet.d.ts +0 -3
- package/dist/spreadsheet/InfiniteSpreadsheet.js +0 -265
- package/dist/spreadsheet/SpreadsheetAddColumnButton.d.ts +0 -6
- package/dist/spreadsheet/SpreadsheetAddColumnButton.js +0 -32
- package/dist/spreadsheet/SpreadsheetCell.js +0 -115
- package/dist/spreadsheet/SpreadsheetHeaderCell.d.ts +0 -18
- package/dist/spreadsheet/SpreadsheetHeaderCell.js +0 -141
- package/dist/spreadsheet/SpreadsheetRow.d.ts +0 -21
- package/dist/spreadsheet/SpreadsheetRow.js +0 -68
- package/dist/spreadsheet/SpreadsheetRowActionCell.js +0 -78
- package/dist/spreadsheet/hooks/useColumnResize.d.ts +0 -6
- package/dist/spreadsheet/hooks/useColumnResize.js +0 -46
- package/dist/spreadsheet/types.d.ts +0 -92
- package/dist/spreadsheet/types.js +0 -21
- /package/dist/{spreadsheet → table}/SpreadsheetCellEditor.d.ts +0 -0
package/dist/charts/PieChart.js
CHANGED
|
@@ -1,183 +1,171 @@
|
|
|
1
|
-
import { useGalaxyTheme
|
|
2
|
-
import Text, { TextSize, TextWeight } from "../text/Text.js";
|
|
1
|
+
import { useGalaxyTheme } from "../theme/GalaxyTheme.js";
|
|
3
2
|
import FlexWrapper, { AlignItems, FlexDirection, JustifyContent } from "../containers/FlexWrapper.js";
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
3
|
+
import { ChartMarkState } from "./types.js";
|
|
4
|
+
import { PIE_CHART_MARGIN, PIE_MIN_SLICE_SHARE } from "./constants.js";
|
|
5
|
+
import { ChartTooltipVerticalAlign } from "./useChartTooltipPosition.js";
|
|
6
|
+
import { useChartInteraction } from "./useChartInteraction.js";
|
|
7
|
+
import { useChartFormatters } from "./useChartFormatters.js";
|
|
8
|
+
import { useChartDimensions } from "./useChartDimensions.js";
|
|
9
|
+
import { buildPieChartGeometry } from "./pieChartGeometry.js";
|
|
10
|
+
import { resolveChartPaletteColor, resolveSeriesColor } from "./chartTheme.js";
|
|
11
|
+
import { ChartArcPath, ChartCenterBox } from "./ChartPrimitives.js";
|
|
12
|
+
import ChartFrame from "./ChartFrame.js";
|
|
13
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
14
|
+
import { jsx } from "react/jsx-runtime";
|
|
15
15
|
//#region src/charts/PieChart.tsx
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
propsAsIs: false,
|
|
25
|
-
vars: {
|
|
26
|
-
"c1ez9f1-0": [_exp()],
|
|
27
|
-
"c1ez9f1-1": [_exp2()]
|
|
28
|
-
}
|
|
29
|
-
}));
|
|
30
|
-
var resolveSliceColor = (slice, index, palette) => slice.color ?? palette[index % palette.length];
|
|
31
|
-
var PieChart = ({ slices, width, height, fillWidth = false, aspectRatio, innerRadius = 0, padAngle = DEFAULT_PAD_ANGLE, cornerRadius = DEFAULT_CORNER_RADIUS, valueFormatter = defaultValueFormatter, labelFormatter = defaultLabelFormatter, tooltipRenderer, noTooltip = false, showLegend = true, showLabels = false, ariaLabel }) => {
|
|
16
|
+
/**
|
|
17
|
+
* Pie and donut chart.
|
|
18
|
+
*
|
|
19
|
+
* Unlike the cartesian charts there is no category axis to hit-test against, so
|
|
20
|
+
* each arc is its own target. Small slices are folded into an "Other" bucket by
|
|
21
|
+
* default, which keeps every remaining arc large enough to actually hit.
|
|
22
|
+
*/
|
|
23
|
+
var PieChart = ({ series, slices, width, height, fillWidth, fillHeight, aspectRatio, margin, innerRadiusRatio, padAngle, cornerRadius, minSliceShare = PIE_MIN_SLICE_SHARE, aggregatedLabel, centerContent, valueUnit, valueFormatter, labelFormatter, showLegend = true, noTooltip = false, tooltipRenderer, isLoading = false, contentWhenEmpty, ariaLabel, ariaDescription, onPinnedChange }) => {
|
|
32
24
|
const { theme } = useGalaxyTheme();
|
|
33
25
|
const containerRef = useRef(null);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
outerRadius,
|
|
48
|
-
cornerRadius
|
|
49
|
-
]);
|
|
50
|
-
const labelArc = useMemo(() => {
|
|
51
|
-
const mid = innerRadius > 0 ? (innerRadius + outerRadius) / 2 : outerRadius * .65;
|
|
52
|
-
return arc().innerRadius(mid).outerRadius(mid);
|
|
53
|
-
}, [innerRadius, outerRadius]);
|
|
54
|
-
const total = useMemo(() => slices.reduce((sum, s) => sum + s.value, 0), [slices]);
|
|
55
|
-
const handleSliceMouseEnter = useCallback((sliceIndex, event) => {
|
|
56
|
-
const slice = slices[sliceIndex];
|
|
57
|
-
if (!slice) return;
|
|
58
|
-
const svgRect = svgRef.current?.getBoundingClientRect();
|
|
59
|
-
if (!svgRect) return;
|
|
60
|
-
setHoveredSliceIndex(sliceIndex);
|
|
61
|
-
setTooltip({
|
|
62
|
-
label: labelFormatter(slice.label),
|
|
63
|
-
items: [{
|
|
64
|
-
seriesKey: slice.label,
|
|
65
|
-
seriesName: labelFormatter(slice.label),
|
|
66
|
-
value: slice.value,
|
|
67
|
-
color: resolveSliceColor(slice, sliceIndex, palette)
|
|
68
|
-
}],
|
|
69
|
-
x: event.clientX - svgRect.left,
|
|
70
|
-
y: event.clientY - svgRect.top
|
|
71
|
-
});
|
|
72
|
-
}, [
|
|
73
|
-
slices,
|
|
74
|
-
palette,
|
|
26
|
+
const dimensions = useChartDimensions({
|
|
27
|
+
containerRef,
|
|
28
|
+
width,
|
|
29
|
+
height,
|
|
30
|
+
fillWidth,
|
|
31
|
+
fillHeight,
|
|
32
|
+
aspectRatio,
|
|
33
|
+
margin: margin ?? PIE_CHART_MARGIN
|
|
34
|
+
});
|
|
35
|
+
const { plotWidth, plotHeight } = dimensions;
|
|
36
|
+
const { formatValue, formatLabel } = useChartFormatters({
|
|
37
|
+
valueFormatter,
|
|
38
|
+
valueUnit,
|
|
75
39
|
labelFormatter
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const handleSliceMouseLeave = useCallback(() => {
|
|
88
|
-
setHoveredSliceIndex(null);
|
|
89
|
-
setTooltip(null);
|
|
90
|
-
}, []);
|
|
91
|
-
const legendSeries = useMemo(() => slices.map((s, i) => ({
|
|
92
|
-
key: s.label,
|
|
93
|
-
name: labelFormatter(s.label),
|
|
94
|
-
color: resolveSliceColor(s, i, palette)
|
|
95
|
-
})), [
|
|
40
|
+
});
|
|
41
|
+
const sliceKeys = useMemo(() => Object.keys(series), [series]);
|
|
42
|
+
const getSliceLabel = useCallback((sliceKey) => series[sliceKey]?.label ?? sliceKey, [series]);
|
|
43
|
+
const getSliceColor = useCallback((sliceKey) => series[sliceKey]?.color, [series]);
|
|
44
|
+
const resolveColor = useCallback(({ sliceKey, index, explicit }) => {
|
|
45
|
+
if (explicit) return resolveChartPaletteColor(theme, explicit);
|
|
46
|
+
if (sliceKey === null) return theme.color.text.tertiary;
|
|
47
|
+
const slotIndex = sliceKeys.indexOf(sliceKey);
|
|
48
|
+
return resolveSeriesColor(theme, slotIndex >= 0 ? slotIndex : index);
|
|
49
|
+
}, [theme, sliceKeys]);
|
|
50
|
+
const geometry = useMemo(() => buildPieChartGeometry({
|
|
96
51
|
slices,
|
|
97
|
-
|
|
98
|
-
|
|
52
|
+
plotWidth,
|
|
53
|
+
plotHeight,
|
|
54
|
+
innerRadiusRatio,
|
|
55
|
+
padAngle,
|
|
56
|
+
cornerRadius,
|
|
57
|
+
minSliceShare,
|
|
58
|
+
aggregatedLabel,
|
|
59
|
+
resolveColor,
|
|
60
|
+
getSliceColor,
|
|
61
|
+
getSliceLabel
|
|
62
|
+
}), [
|
|
63
|
+
slices,
|
|
64
|
+
plotWidth,
|
|
65
|
+
plotHeight,
|
|
66
|
+
innerRadiusRatio,
|
|
67
|
+
padAngle,
|
|
68
|
+
cornerRadius,
|
|
69
|
+
minSliceShare,
|
|
70
|
+
aggregatedLabel,
|
|
71
|
+
resolveColor,
|
|
72
|
+
getSliceColor,
|
|
73
|
+
getSliceLabel
|
|
99
74
|
]);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
75
|
+
const isEmpty = geometry.arcs.length === 0;
|
|
76
|
+
const legendItems = useMemo(() => geometry.arcs.map((chartArc) => ({
|
|
77
|
+
key: chartArc.id,
|
|
78
|
+
label: formatLabel(chartArc.label),
|
|
79
|
+
color: chartArc.color
|
|
80
|
+
})), [geometry.arcs, formatLabel]);
|
|
81
|
+
const legendKeys = useMemo(() => legendItems.map((item) => item.key), [legendItems]);
|
|
82
|
+
const interaction = useChartInteraction({
|
|
83
|
+
categoryCount: geometry.arcs.length,
|
|
84
|
+
seriesKeys: legendKeys,
|
|
85
|
+
disabled: noTooltip || isLoading || isEmpty,
|
|
86
|
+
onPinnedChange
|
|
103
87
|
});
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
data: tooltip,
|
|
170
|
-
valueFormatter
|
|
171
|
-
}))]
|
|
172
|
-
}), showLegend && /* @__PURE__ */ jsx(FlexWrapper, {
|
|
173
|
-
direction: FlexDirection.ROW,
|
|
174
|
-
gap: 0,
|
|
175
|
-
children: /* @__PURE__ */ jsx(ChartLegend, {
|
|
176
|
-
series: legendSeries,
|
|
177
|
-
palette
|
|
178
|
-
})
|
|
179
|
-
})]
|
|
88
|
+
const tooltipModel = useMemo(() => {
|
|
89
|
+
if (noTooltip || interaction.activeIndex === null) return null;
|
|
90
|
+
const chartArc = geometry.arcs[interaction.activeIndex];
|
|
91
|
+
if (!chartArc) return null;
|
|
92
|
+
return {
|
|
93
|
+
label: formatLabel(chartArc.label),
|
|
94
|
+
rows: [{
|
|
95
|
+
key: chartArc.id,
|
|
96
|
+
label: `${Math.round(chartArc.share * 1e3) / 10}% of total`,
|
|
97
|
+
value: chartArc.value,
|
|
98
|
+
formattedValue: formatValue(chartArc.value),
|
|
99
|
+
color: chartArc.color
|
|
100
|
+
}],
|
|
101
|
+
anchorX: chartArc.anchorX,
|
|
102
|
+
anchorY: chartArc.anchorY
|
|
103
|
+
};
|
|
104
|
+
}, [
|
|
105
|
+
noTooltip,
|
|
106
|
+
interaction.activeIndex,
|
|
107
|
+
geometry.arcs,
|
|
108
|
+
formatValue,
|
|
109
|
+
formatLabel
|
|
110
|
+
]);
|
|
111
|
+
const showCenter = centerContent && (innerRadiusRatio ?? .62) > 0;
|
|
112
|
+
return /* @__PURE__ */ jsx(ChartFrame, {
|
|
113
|
+
containerRef,
|
|
114
|
+
dimensions,
|
|
115
|
+
fillWidth,
|
|
116
|
+
fillHeight,
|
|
117
|
+
interaction,
|
|
118
|
+
ariaLabel: ariaLabel ?? `Pie chart with ${geometry.arcs.length} slices`,
|
|
119
|
+
ariaDescription,
|
|
120
|
+
noTooltip,
|
|
121
|
+
tooltipModel,
|
|
122
|
+
tooltipRenderer,
|
|
123
|
+
placeAwayFromX: geometry.centerX,
|
|
124
|
+
verticalAlign: ChartTooltipVerticalAlign.CENTERED,
|
|
125
|
+
isEmpty,
|
|
126
|
+
isLoading,
|
|
127
|
+
contentWhenEmpty,
|
|
128
|
+
showLegend,
|
|
129
|
+
legendItems,
|
|
130
|
+
legendInset: 0,
|
|
131
|
+
plotOverlay: showCenter && !isEmpty ? /* @__PURE__ */ jsx(ChartDonutCenter, {
|
|
132
|
+
centerX: dimensions.margin.left + geometry.centerX,
|
|
133
|
+
centerY: dimensions.margin.top + geometry.centerY,
|
|
134
|
+
diameter: geometry.innerRadius * 2,
|
|
135
|
+
children: centerContent
|
|
136
|
+
}) : null,
|
|
137
|
+
children: /* @__PURE__ */ jsx("g", {
|
|
138
|
+
transform: `translate(${geometry.centerX}, ${geometry.centerY})`,
|
|
139
|
+
children: geometry.arcs.map((chartArc, index) => /* @__PURE__ */ jsx(ChartArcPath, {
|
|
140
|
+
d: chartArc.path,
|
|
141
|
+
fill: chartArc.color,
|
|
142
|
+
"data-state": isLoading ? ChartMarkState.MUTED : interaction.getMarkState({
|
|
143
|
+
categoryIndex: index,
|
|
144
|
+
seriesKey: chartArc.id
|
|
145
|
+
}),
|
|
146
|
+
"data-interactive": noTooltip || isLoading ? "false" : "true",
|
|
147
|
+
onMouseEnter: () => interaction.onCategoryEnter(index),
|
|
148
|
+
onMouseLeave: interaction.onCategoryLeave,
|
|
149
|
+
onClick: () => interaction.onCategoryClick(index),
|
|
150
|
+
"aria-label": `${chartArc.label}: ${formatValue(chartArc.value)}`
|
|
151
|
+
}, chartArc.id))
|
|
152
|
+
})
|
|
180
153
|
});
|
|
181
154
|
};
|
|
155
|
+
/** Positions arbitrary content inside the donut hole. */
|
|
156
|
+
var ChartDonutCenter = ({ centerX, centerY, diameter, children }) => /* @__PURE__ */ jsx(ChartCenterBox, {
|
|
157
|
+
$centerX: centerX,
|
|
158
|
+
$centerY: centerY,
|
|
159
|
+
$diameter: diameter,
|
|
160
|
+
children: /* @__PURE__ */ jsx(FlexWrapper, {
|
|
161
|
+
direction: FlexDirection.COLUMN,
|
|
162
|
+
justifyContent: JustifyContent.CENTER,
|
|
163
|
+
alignItems: AlignItems.CENTER,
|
|
164
|
+
gap: 2,
|
|
165
|
+
fillWidth: true,
|
|
166
|
+
fillHeight: true,
|
|
167
|
+
children
|
|
168
|
+
})
|
|
169
|
+
});
|
|
182
170
|
//#endregion
|
|
183
171
|
export { PieChart as default };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { buildValueScale } from "./chartScales";
|
|
2
|
+
import { BarChartNormalization, BarCorners } from "./types";
|
|
3
|
+
import type { BarChartGroupBand, BarChartGroupDatum, BarChartRect, ChartPalette, ChartValueDomain } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Bar chart geometry.
|
|
6
|
+
*
|
|
7
|
+
* Flattens the group → bar → component hierarchy into a flat array of
|
|
8
|
+
* rectangles plus one hit band per group, so the component body is a `.map()`
|
|
9
|
+
* with no nested loops or running accumulators.
|
|
10
|
+
*
|
|
11
|
+
* Pure: no React, no theme access. Colors arrive through a resolver callback so
|
|
12
|
+
* this module never has to know how the palette maps to the theme.
|
|
13
|
+
*/
|
|
14
|
+
interface BarSpacing {
|
|
15
|
+
/** Px between neighbouring groups. */
|
|
16
|
+
groupGap: number;
|
|
17
|
+
/** Px between bars inside a group. */
|
|
18
|
+
barGap: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Pick gap sizes from how crowded the chart is.
|
|
22
|
+
*
|
|
23
|
+
* Sparse charts stay roomy regardless of width; once there are enough bars to
|
|
24
|
+
* matter, the gaps tighten as the plot narrows.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getBarSpacing(plotWidth: number, totalBars: number): BarSpacing;
|
|
27
|
+
/**
|
|
28
|
+
* Rectangle with one pair of corners rounded.
|
|
29
|
+
*
|
|
30
|
+
* The radius is clamped to half the height as well as half the width, so a very
|
|
31
|
+
* short bar renders as a symmetric cap instead of a lopsided half-pill.
|
|
32
|
+
*/
|
|
33
|
+
export declare function roundedRectPath(x: number, y: number, width: number, height: number, radius: number, corners: BarCorners): string;
|
|
34
|
+
/**
|
|
35
|
+
* Signed value domain across every group.
|
|
36
|
+
*
|
|
37
|
+
* Percent-normalized charts always span 0–1, so the axis is stable no matter
|
|
38
|
+
* what the underlying totals are.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getBarValueDomain<TMetric extends string, TPivot extends string, TComponentKey extends string>(groups: BarChartGroupDatum<TMetric, TPivot, TComponentKey>[], normalization: BarChartNormalization): [number, number];
|
|
41
|
+
export interface BarChartGeometry<TMetric extends string, TComponentKey extends string = string> {
|
|
42
|
+
/** Every rendered rectangle, in draw order. */
|
|
43
|
+
rects: BarChartRect<TMetric, TComponentKey>[];
|
|
44
|
+
/** One hit target per group, tiling the plot with no gaps. */
|
|
45
|
+
bands: BarChartGroupBand[];
|
|
46
|
+
/** The resolved value scale, shared with the axis. */
|
|
47
|
+
valueScale: ReturnType<typeof buildValueScale>;
|
|
48
|
+
/** Pixel y of value zero — where bars meet the baseline. */
|
|
49
|
+
baselineY: number;
|
|
50
|
+
}
|
|
51
|
+
interface BuildBarChartGeometryArgs<TMetric extends string, TPivot extends string, TComponentKey extends string> {
|
|
52
|
+
groups: BarChartGroupDatum<TMetric, TPivot, TComponentKey>[];
|
|
53
|
+
plotWidth: number;
|
|
54
|
+
plotHeight: number;
|
|
55
|
+
normalization: BarChartNormalization;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the chart as a whole is stacked.
|
|
58
|
+
*
|
|
59
|
+
* Chart-wide, not per bar, because it decides the *colour rule* and the legend
|
|
60
|
+
* lists one domain for the whole chart. Deriving it per bar would colour a
|
|
61
|
+
* single-component bar by metric while the legend named components.
|
|
62
|
+
*/
|
|
63
|
+
isStacked: boolean;
|
|
64
|
+
valueDomain?: Partial<ChartValueDomain>;
|
|
65
|
+
/**
|
|
66
|
+
* Maps one segment to a concrete color.
|
|
67
|
+
*
|
|
68
|
+
* The geometry deliberately does not decide the rule — it only reports what
|
|
69
|
+
* it knows about the segment. Whether a stack is coloured by component or a
|
|
70
|
+
* group by metric is a presentation question, and it needs the `series`
|
|
71
|
+
* record to answer, which this module has no business importing.
|
|
72
|
+
*/
|
|
73
|
+
resolveColor: (args: ResolveBarColorArgs<TMetric>) => string;
|
|
74
|
+
}
|
|
75
|
+
export interface ResolveBarColorArgs<TMetric extends string> {
|
|
76
|
+
metric: TMetric;
|
|
77
|
+
/** Index of the bar within its group. */
|
|
78
|
+
barIndex: number;
|
|
79
|
+
/** Index of the component within its bar. */
|
|
80
|
+
componentIndex: number;
|
|
81
|
+
/** True when the *chart* is stacked — see the option of the same name. */
|
|
82
|
+
isStacked: boolean;
|
|
83
|
+
/** The component's own palette override, if it declared one. */
|
|
84
|
+
explicit?: ChartPalette;
|
|
85
|
+
}
|
|
86
|
+
export declare function buildBarChartGeometry<TMetric extends string, TPivot extends string = string, TComponentKey extends string = string>({ groups, plotWidth, plotHeight, normalization, isStacked, valueDomain, resolveColor, }: BuildBarChartGeometryArgs<TMetric, TPivot, TComponentKey>): BarChartGeometry<TMetric, TComponentKey>;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { BarChartNormalization, BarCorners } from "./types.js";
|
|
2
|
+
import { BAR_WIDTH_RATIO, MAX_GAP_SHARE } from "./constants.js";
|
|
3
|
+
import { applyDomainOverride, buildValueScale, sanitizeValue } from "./chartScales.js";
|
|
4
|
+
//#region src/charts/barChartGeometry.ts
|
|
5
|
+
/**
|
|
6
|
+
* Pick gap sizes from how crowded the chart is.
|
|
7
|
+
*
|
|
8
|
+
* Sparse charts stay roomy regardless of width; once there are enough bars to
|
|
9
|
+
* matter, the gaps tighten as the plot narrows.
|
|
10
|
+
*/
|
|
11
|
+
function getBarSpacing(plotWidth, totalBars) {
|
|
12
|
+
if (totalBars < 13 || plotWidth > 650) return {
|
|
13
|
+
groupGap: 24,
|
|
14
|
+
barGap: 6
|
|
15
|
+
};
|
|
16
|
+
if (plotWidth > 500) return {
|
|
17
|
+
groupGap: 16,
|
|
18
|
+
barGap: 4
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
groupGap: 12,
|
|
22
|
+
barGap: 3
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Rectangle with one pair of corners rounded.
|
|
27
|
+
*
|
|
28
|
+
* The radius is clamped to half the height as well as half the width, so a very
|
|
29
|
+
* short bar renders as a symmetric cap instead of a lopsided half-pill.
|
|
30
|
+
*/
|
|
31
|
+
function roundedRectPath(x, y, width, height, radius, corners) {
|
|
32
|
+
const r = corners === BarCorners.NONE ? 0 : Math.max(0, Math.min(radius, width / 2, height / 2));
|
|
33
|
+
if (r === 0) return `M${x},${y} h${width} v${height} h${-width} z`;
|
|
34
|
+
if (corners === BarCorners.TOP) return [
|
|
35
|
+
`M${x},${y + r}`,
|
|
36
|
+
`a${r},${r} 0 0 1 ${r},${-r}`,
|
|
37
|
+
`h${width - 2 * r}`,
|
|
38
|
+
`a${r},${r} 0 0 1 ${r},${r}`,
|
|
39
|
+
`v${height - r}`,
|
|
40
|
+
`h${-width}`,
|
|
41
|
+
`z`
|
|
42
|
+
].join(" ");
|
|
43
|
+
return [
|
|
44
|
+
`M${x},${y}`,
|
|
45
|
+
`h${width}`,
|
|
46
|
+
`v${height - r}`,
|
|
47
|
+
`a${r},${r} 0 0 1 ${-r},${r}`,
|
|
48
|
+
`h${-(width - 2 * r)}`,
|
|
49
|
+
`a${r},${r} 0 0 1 ${-r},${-r}`,
|
|
50
|
+
`z`
|
|
51
|
+
].join(" ");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Turn one bar's components into stacked `[from, to]` intervals.
|
|
55
|
+
*
|
|
56
|
+
* Positive and negative values accumulate on separate cursors, which is what
|
|
57
|
+
* lets a bar mixing both stack upward and downward from the baseline without
|
|
58
|
+
* any special-casing downstream — and is also why percent normalization is a
|
|
59
|
+
* one-line division rather than a second code path.
|
|
60
|
+
*/
|
|
61
|
+
function stackComponents(values, normalization) {
|
|
62
|
+
let positiveCursor = 0;
|
|
63
|
+
let negativeCursor = 0;
|
|
64
|
+
const intervals = values.map((value) => {
|
|
65
|
+
if (value >= 0) {
|
|
66
|
+
const from = positiveCursor;
|
|
67
|
+
positiveCursor += value;
|
|
68
|
+
return {
|
|
69
|
+
value,
|
|
70
|
+
from,
|
|
71
|
+
to: positiveCursor
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const to = negativeCursor;
|
|
75
|
+
negativeCursor += value;
|
|
76
|
+
return {
|
|
77
|
+
value,
|
|
78
|
+
from: negativeCursor,
|
|
79
|
+
to
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
if (normalization !== BarChartNormalization.PERCENT) return intervals;
|
|
83
|
+
const magnitude = positiveCursor - negativeCursor;
|
|
84
|
+
if (magnitude === 0) return intervals;
|
|
85
|
+
return intervals.map((interval) => ({
|
|
86
|
+
value: interval.value,
|
|
87
|
+
from: interval.from / magnitude,
|
|
88
|
+
to: interval.to / magnitude
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Signed value domain across every group.
|
|
93
|
+
*
|
|
94
|
+
* Percent-normalized charts always span 0–1, so the axis is stable no matter
|
|
95
|
+
* what the underlying totals are.
|
|
96
|
+
*/
|
|
97
|
+
function getBarValueDomain(groups, normalization) {
|
|
98
|
+
if (normalization === BarChartNormalization.PERCENT) return [0, 1];
|
|
99
|
+
let min = 0;
|
|
100
|
+
let max = 0;
|
|
101
|
+
for (const group of groups) for (const bar of group.bars) {
|
|
102
|
+
let positive = 0;
|
|
103
|
+
let negative = 0;
|
|
104
|
+
for (const component of bar.components) {
|
|
105
|
+
const value = sanitizeValue(component.value);
|
|
106
|
+
if (value >= 0) positive += value;
|
|
107
|
+
else negative += value;
|
|
108
|
+
}
|
|
109
|
+
if (positive > max) max = positive;
|
|
110
|
+
if (negative < min) min = negative;
|
|
111
|
+
}
|
|
112
|
+
return [min, max];
|
|
113
|
+
}
|
|
114
|
+
function buildBarChartGeometry({ groups, plotWidth, plotHeight, normalization, isStacked, valueDomain, resolveColor }) {
|
|
115
|
+
const valueScale = buildValueScale(applyDomainOverride(getBarValueDomain(groups, normalization), valueDomain), [plotHeight, 0]);
|
|
116
|
+
const baselineY = valueScale(0);
|
|
117
|
+
const rects = [];
|
|
118
|
+
const bands = [];
|
|
119
|
+
if (groups.length === 0 || plotWidth <= 0 || plotHeight <= 0) return {
|
|
120
|
+
rects,
|
|
121
|
+
bands,
|
|
122
|
+
valueScale,
|
|
123
|
+
baselineY
|
|
124
|
+
};
|
|
125
|
+
const { groupGap, barGap } = getBarSpacing(plotWidth, groups.reduce((sum, group) => sum + group.bars.length, 0));
|
|
126
|
+
const step = plotWidth / groups.length;
|
|
127
|
+
const effectiveGroupGap = Math.min(groupGap, step * MAX_GAP_SHARE);
|
|
128
|
+
const groupWidth = Math.max(1, step - effectiveGroupGap);
|
|
129
|
+
groups.forEach((group, groupIndex) => {
|
|
130
|
+
const bandX = groupIndex * step;
|
|
131
|
+
const groupX = bandX + (step - groupWidth) / 2;
|
|
132
|
+
const barCount = Math.max(1, group.bars.length);
|
|
133
|
+
const effectiveBarGap = Math.min(barGap, groupWidth / barCount * MAX_GAP_SHARE);
|
|
134
|
+
const slotWidth = (groupWidth - (barCount - 1) * effectiveBarGap) / barCount;
|
|
135
|
+
const barWidth = Math.max(1, Math.min(slotWidth * BAR_WIDTH_RATIO, 44));
|
|
136
|
+
const radius = barWidth < 40 ? 2 : 4;
|
|
137
|
+
const barsStartX = groupX + (groupWidth - (barCount * barWidth + (barCount - 1) * effectiveBarGap)) / 2;
|
|
138
|
+
let groupTopY = baselineY;
|
|
139
|
+
let hasValue = false;
|
|
140
|
+
group.bars.forEach((bar, barIndex) => {
|
|
141
|
+
const barX = barsStartX + barIndex * (barWidth + effectiveBarGap);
|
|
142
|
+
const values = bar.components.map((component) => sanitizeValue(component.value));
|
|
143
|
+
const intervals = stackComponents(values, normalization);
|
|
144
|
+
let firstPositive = -1;
|
|
145
|
+
let lastPositive = -1;
|
|
146
|
+
let firstNegative = -1;
|
|
147
|
+
let lastNegative = -1;
|
|
148
|
+
values.forEach((value, index) => {
|
|
149
|
+
if (value > 0) {
|
|
150
|
+
if (firstPositive === -1) firstPositive = index;
|
|
151
|
+
lastPositive = index;
|
|
152
|
+
}
|
|
153
|
+
if (value < 0) {
|
|
154
|
+
if (firstNegative === -1) firstNegative = index;
|
|
155
|
+
lastNegative = index;
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
bar.components.forEach((component, componentIndex) => {
|
|
159
|
+
const { value, from, to } = intervals[componentIndex];
|
|
160
|
+
if (value === 0) return;
|
|
161
|
+
hasValue = true;
|
|
162
|
+
const isNegative = value < 0;
|
|
163
|
+
const isCap = componentIndex === (isNegative ? lastNegative : lastPositive);
|
|
164
|
+
const fromY = Math.round(valueScale(from));
|
|
165
|
+
const toY = Math.round(valueScale(to));
|
|
166
|
+
const topY = Math.min(fromY, toY);
|
|
167
|
+
const rawHeight = Math.abs(fromY - toY);
|
|
168
|
+
const isStackedBar = bar.components.length > 1;
|
|
169
|
+
const shave = componentIndex === (isNegative ? firstNegative : firstPositive) ? 0 : 2;
|
|
170
|
+
const height = Math.max(isStackedBar ? 0 : 2, rawHeight - shave);
|
|
171
|
+
const y = isNegative ? topY + shave : topY;
|
|
172
|
+
if (!isNegative && y < groupTopY) groupTopY = y;
|
|
173
|
+
rects.push({
|
|
174
|
+
id: `${groupIndex}:${barIndex}:${component.key}`,
|
|
175
|
+
groupIndex,
|
|
176
|
+
barIndex,
|
|
177
|
+
metric: bar.metric,
|
|
178
|
+
componentKey: component.key,
|
|
179
|
+
label: component.label,
|
|
180
|
+
value,
|
|
181
|
+
valueRange: [from, to],
|
|
182
|
+
x: barX,
|
|
183
|
+
y,
|
|
184
|
+
width: barWidth,
|
|
185
|
+
height,
|
|
186
|
+
color: resolveColor({
|
|
187
|
+
metric: bar.metric,
|
|
188
|
+
barIndex,
|
|
189
|
+
componentIndex,
|
|
190
|
+
isStacked,
|
|
191
|
+
explicit: component.color
|
|
192
|
+
}),
|
|
193
|
+
corners: isCap ? isNegative ? BarCorners.BOTTOM : BarCorners.TOP : BarCorners.NONE,
|
|
194
|
+
radius
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
bands.push({
|
|
199
|
+
groupIndex,
|
|
200
|
+
label: group.label,
|
|
201
|
+
x: bandX,
|
|
202
|
+
y: 0,
|
|
203
|
+
width: step,
|
|
204
|
+
height: plotHeight,
|
|
205
|
+
anchorX: bandX + step / 2,
|
|
206
|
+
anchorY: groupTopY,
|
|
207
|
+
isEmpty: !hasValue
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
return {
|
|
211
|
+
rects,
|
|
212
|
+
bands,
|
|
213
|
+
valueScale,
|
|
214
|
+
baselineY
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
export { buildBarChartGeometry, getBarSpacing, getBarValueDomain, roundedRectPath };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ChartValueUnit } from "./types";
|
|
2
|
+
import type { ChartLabelFormatter, ChartValueFormatter } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Format a value with a built-in unit.
|
|
5
|
+
*
|
|
6
|
+
* `PERCENT` takes a 0–100 value rather than a 0–1 fraction, matching how
|
|
7
|
+
* percentage data usually arrives, and appends the sign itself instead of using
|
|
8
|
+
* `style: "percent"` (which would divide by 100).
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatChartValue(value: number, unit: ChartValueUnit): string;
|
|
11
|
+
/**
|
|
12
|
+
* Pick the effective value formatter.
|
|
13
|
+
*
|
|
14
|
+
* An explicit `valueFormatter` always wins; otherwise the unit supplies a
|
|
15
|
+
* default, and `PLAIN` is the fallback when neither is given.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveValueFormatter(valueFormatter?: ChartValueFormatter, unit?: ChartValueUnit): ChartValueFormatter;
|
|
18
|
+
/** Pick the effective label formatter, defaulting to identity. */
|
|
19
|
+
export declare function resolveLabelFormatter(labelFormatter?: ChartLabelFormatter): ChartLabelFormatter;
|