[DASHBOARDS] Rotate ticks on bar and line charts (#16528)
## Description 3 steps depending on the widget width. From bigger to tighter space: - Fully shown horizontal text - Rotated text - Rotated text with skipped ticks to avoid overlapping Also created common files for all constants for bar and pie charts. Since a lot of them are shared, they can be inherited from a common file. ## Video QA https://github.com/user-attachments/assets/fd58d412-1a8b-4bd6-a420-4c03767e98d5
This commit is contained in:
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { type AggregateChartOperation } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/types/AggregateChartOperation';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useW
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { DateAggregateOperations } from '@/object-record/record-table/constants/DateAggregateOperations';
|
||||
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { type AggregateChartOperation } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/types/AggregateChartOperation';
|
||||
import { getAggregateChartOperationLabel } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAggregateChartOperationLabel';
|
||||
import { getAvailableAggregateOperationsForAggregateChart } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/utils/getAvailableAggregateOperationsForAggregateChart';
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
import { getDateGranularityPluralLabel } from '@/command-menu/pages/page-layout/utils/getDateGranularityPluralLabel';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { PIE_CHART_MAXIMUM_NUMBER_OF_SLICES } from '@/page-layout/widgets/graph/graphWidgetPieChart/constants/PieChartMaximumNumberOfSlices.constant';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
@@ -24,10 +24,10 @@ export const getChartLimitMessage = ({
|
||||
}: GetChartLimitMessageParams): string => {
|
||||
const maxItems =
|
||||
graphType === GraphType.LINE
|
||||
? LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS
|
||||
? LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS
|
||||
: graphType === GraphType.VERTICAL_BAR ||
|
||||
graphType === GraphType.HORIZONTAL_BAR
|
||||
? BAR_CHART_MAXIMUM_NUMBER_OF_BARS
|
||||
? BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS
|
||||
: PIE_CHART_MAXIMUM_NUMBER_OF_SLICES;
|
||||
|
||||
if (isPrimaryAxisDate && isDefined(primaryAxisDateGranularity)) {
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
|
||||
import { GraphWidgetLegendDot } from '@/page-layout/widgets/graph/components/GraphWidgetLegendDot';
|
||||
import { LEGEND_HIGHLIGHT_DIMMED_OPACITY } from '@/page-layout/widgets/graph/constants/LegendHighlightDimmedOpacity.constant';
|
||||
import { LEGEND_ITEM_ESTIMATED_WIDTH } from '@/page-layout/widgets/graph/constants/LegendItemEstimatedWidth.constant';
|
||||
import { LEGEND_LABEL_MAX_WIDTH } from '@/page-layout/widgets/graph/constants/LegendLabelMaxWidth.constant';
|
||||
import { LEGEND_PAGINATION_CONTROLS_WIDTH } from '@/page-layout/widgets/graph/constants/LegendPaginationControlsWidth.constant';
|
||||
import { LEGEND_ITEM_ESTIMATED_WIDTH } from '@/page-layout/widgets/graph/constants/LegendItemEstimatedWidth';
|
||||
import { LEGEND_LABEL_MAX_WIDTH } from '@/page-layout/widgets/graph/constants/LegendLabelMaxWidth';
|
||||
import { LEGEND_PAGINATION_CONTROLS_WIDTH } from '@/page-layout/widgets/graph/constants/LegendPaginationControlsWidth';
|
||||
import { useLegendItemToggle } from '@/page-layout/widgets/graph/hooks/useLegendItemToggle';
|
||||
import { graphWidgetHiddenLegendIdsComponentState } from '@/page-layout/widgets/graph/states/graphWidgetHiddenLegendIdsComponentState';
|
||||
import { graphWidgetHighlightedLegendIdComponentState } from '@/page-layout/widgets/graph/states/graphWidgetHighlightedLegendIdComponentState';
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
export const COMMON_CHART_CONSTANTS = {
|
||||
MINIMUM_WIDTH_PER_TICK_ROTATED: 20,
|
||||
ROTATED_LABELS_EXTRA_BOTTOM_MARGIN: 20,
|
||||
MARGIN_TOP: 20,
|
||||
MARGIN_RIGHT: 20,
|
||||
MARGIN_LEFT_WITHOUT_LABEL: 65,
|
||||
MARGIN_LEFT_WITH_LABEL: 80,
|
||||
MARGIN_BOTTOM_WITHOUT_LABEL: 50,
|
||||
MARGIN_BOTTOM_WITH_LABEL: 65,
|
||||
TICK_PADDING: 5,
|
||||
TICK_SIZE: 0,
|
||||
AXIS_FONT_SIZE: 11,
|
||||
LEFT_AXIS_LEGEND_OFFSET_PADDING: 5,
|
||||
BOTTOM_AXIS_LEGEND_OFFSET: 55,
|
||||
TICK_MINIMUM_NUMBER_OF_DISPLAYED_CHARACTERS: 10,
|
||||
HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO: 0.6,
|
||||
ROTATED_LABEL_CHARACTER_WIDTH_RATIO: 0.5,
|
||||
TICK_ROTATION_ANGLE: -45,
|
||||
TICK_PADDING_ALLOWANCE: 10,
|
||||
MIN_TICK_LABEL_LENGTH: 5,
|
||||
POSITIVE_RANGE_PADDING_RATIO: 0.1,
|
||||
MINIMUM_POSITIVE_RANGE_PADDING: 1,
|
||||
NO_ROTATION_ANGLE: 0,
|
||||
} as const;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const ROTATION_THRESHOLD_WIDTH = 50;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { type DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { type DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
|
||||
export type AggregateChartOperation =
|
||||
| ExtendedAggregateOperations
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
import { getAvailableAggregateOperationsForAggregateChart } from '../getAvailableAggregateOperationsForAggregateChart';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { type AggregateChartOperation } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/types/AggregateChartOperation';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio.constant';
|
||||
import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio';
|
||||
import { type AggregateChartOperation } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/types/AggregateChartOperation';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
|
||||
+7
-6
@@ -1,6 +1,5 @@
|
||||
import { LEGEND_HIGHLIGHT_DIMMED_OPACITY } from '@/page-layout/widgets/graph/constants/LegendHighlightDimmedOpacity.constant';
|
||||
import { BAR_CHART_HOVER_BRIGHTNESS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartHoverBrightness';
|
||||
import { BAR_CHART_MAXIMUM_WIDTH } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/MaximumBarWidth';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { graphWidgetHighlightedLegendIdComponentState } from '@/page-layout/widgets/graph/states/graphWidgetHighlightedLegendIdComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
@@ -33,7 +32,9 @@ const StyledBarRect = styled(animated.rect)<{
|
||||
|
||||
&:hover {
|
||||
filter: ${({ $isInteractive }) =>
|
||||
$isInteractive ? `brightness(${BAR_CHART_HOVER_BRIGHTNESS})` : 'none'};
|
||||
$isInteractive
|
||||
? `brightness(${BAR_CHART_CONSTANTS.HOVER_BRIGHTNESS})`
|
||||
: 'none'};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -152,12 +153,12 @@ export const CustomBarItem = <D extends BarDatum>({
|
||||
|
||||
const constrainedThicknessDimension = to(
|
||||
unconstrainedThicknessDimension,
|
||||
(dimension) => Math.min(dimension, BAR_CHART_MAXIMUM_WIDTH),
|
||||
(dimension) => Math.min(dimension, BAR_CHART_CONSTANTS.MAXIMUM_WIDTH),
|
||||
);
|
||||
|
||||
const centeringOffset = to(unconstrainedThicknessDimension, (dimension) =>
|
||||
dimension > BAR_CHART_MAXIMUM_WIDTH
|
||||
? (dimension - BAR_CHART_MAXIMUM_WIDTH) / 2
|
||||
dimension > BAR_CHART_CONSTANTS.MAXIMUM_WIDTH
|
||||
? (dimension - BAR_CHART_CONSTANTS.MAXIMUM_WIDTH) / 2
|
||||
: 0,
|
||||
);
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { GraphWidgetFloatingTooltip } from '@/page-layout/widgets/graph/components/GraphWidgetFloatingTooltip';
|
||||
import { BAR_CHART_TOOLTIP_OFFSET_PX } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartTooltipOffsetPx';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { graphWidgetBarTooltipComponentState } from '@/page-layout/widgets/graph/graphWidgetBarChart/states/graphWidgetBarTooltipComponentState';
|
||||
import { type BarChartEnrichedKey } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartEnrichedKey';
|
||||
import { getBarChartTooltipData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTooltipData';
|
||||
@@ -63,7 +63,7 @@ export const GraphBarChartTooltip = ({
|
||||
<GraphWidgetFloatingTooltip
|
||||
reference={reference}
|
||||
boundary={containerElement}
|
||||
tooltipOffsetFromAnchorInPx={BAR_CHART_TOOLTIP_OFFSET_PX}
|
||||
tooltipOffsetFromAnchorInPx={BAR_CHART_CONSTANTS.TOOLTIP_OFFSET_PX}
|
||||
items={tooltipData?.tooltipItems ?? []}
|
||||
indexLabel={tooltipData?.indexLabel}
|
||||
highlightedKey={tooltipData?.hoveredKey}
|
||||
|
||||
+14
-19
@@ -4,7 +4,7 @@ import { NoDataLayer } from '@/page-layout/widgets/graph/components/NoDataLayer'
|
||||
import { CustomBarItem } from '@/page-layout/widgets/graph/graphWidgetBarChart/components/CustomBarItem';
|
||||
import { CustomTotalsLayer } from '@/page-layout/widgets/graph/graphWidgetBarChart/components/CustomTotalsLayer';
|
||||
import { GraphBarChartTooltip } from '@/page-layout/widgets/graph/graphWidgetBarChart/components/GraphBarChartTooltip';
|
||||
import { BAR_CHART_OUTER_PADDING_RATIO } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartOuterPaddingRatio';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { useBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartData';
|
||||
import { useBarChartTheme } from '@/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTheme';
|
||||
import { graphWidgetBarTooltipComponentState } from '@/page-layout/widgets/graph/graphWidgetBarChart/states/graphWidgetBarTooltipComponentState';
|
||||
@@ -15,7 +15,6 @@ import { calculateValueRangeFromBarChartKeys } from '@/page-layout/widgets/graph
|
||||
import { getBarChartAxisConfigs } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartAxisConfigs';
|
||||
import { getBarChartColor } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartColor';
|
||||
import { getBarChartInnerPadding } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartInnerPadding';
|
||||
import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins';
|
||||
import { getBarChartTickConfig } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig';
|
||||
import { computeEffectiveValueRange } from '@/page-layout/widgets/graph/utils/computeEffectiveValueRange';
|
||||
import { computeValueTickValues } from '@/page-layout/widgets/graph/utils/computeValueTickValues';
|
||||
@@ -185,20 +184,18 @@ export const GraphWidgetBarChart = ({
|
||||
debouncedHideTooltip();
|
||||
}, [debouncedHideTooltip]);
|
||||
|
||||
const { axisBottom: axisBottomConfig, axisLeft: axisLeftConfig } =
|
||||
getBarChartAxisConfigs({
|
||||
width: chartWidth,
|
||||
height: chartHeight,
|
||||
data,
|
||||
layout,
|
||||
indexBy,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
formatOptions,
|
||||
axisFontSize: chartTheme.axis.ticks.text.fontSize,
|
||||
valueTickValues,
|
||||
tickConfig,
|
||||
});
|
||||
const {
|
||||
axisBottom: axisBottomConfig,
|
||||
axisLeft: axisLeftConfig,
|
||||
margins,
|
||||
} = getBarChartAxisConfigs({
|
||||
layout,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
formatOptions,
|
||||
valueTickValues,
|
||||
tickConfig,
|
||||
});
|
||||
|
||||
const BarItemWithContext = useMemo(
|
||||
() => (props: BarItemProps<BarDatum>) => (
|
||||
@@ -260,8 +257,6 @@ export const GraphWidgetBarChart = ({
|
||||
]
|
||||
: undefined;
|
||||
|
||||
const margins = getBarChartMargins({ xAxisLabel, yAxisLabel, layout });
|
||||
|
||||
return (
|
||||
<StyledContainer id={id}>
|
||||
<GraphWidgetChartContainer
|
||||
@@ -282,7 +277,7 @@ export const GraphWidgetBarChart = ({
|
||||
keys={visibleKeys}
|
||||
indexBy={indexBy}
|
||||
margin={margins}
|
||||
padding={BAR_CHART_OUTER_PADDING_RATIO}
|
||||
padding={BAR_CHART_CONSTANTS.OUTER_PADDING_RATIO}
|
||||
groupMode={groupMode}
|
||||
layout={layout}
|
||||
valueScale={{
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
|
||||
export const BAR_CHART_CONSTANTS = {
|
||||
...COMMON_CHART_CONSTANTS,
|
||||
MAXIMUM_NUMBER_OF_BARS: 100,
|
||||
MAXIMUM_NUMBER_OF_GROUPS_PER_BAR: 50,
|
||||
MAXIMUM_WIDTH: 32,
|
||||
OUTER_PADDING_RATIO: 0.05,
|
||||
DEFAULT_INNER_PADDING: 4,
|
||||
MAXIMUM_VALUE_TICK_COUNT: 6,
|
||||
MINIMUM_VALUE_TICK_COUNT: 2,
|
||||
MINIMUM_WIDTH_PER_TICK: 100,
|
||||
MIN_TICK_SPACING_HEIGHT_RATIO: 2.5,
|
||||
TOOLTIP_OFFSET_PX: 2,
|
||||
TOOLTIP_SCROLLABLE_ITEM_THRESHOLD: 5,
|
||||
HOVER_BRIGHTNESS: 0.85,
|
||||
MINIMUM_BAR_WIDTH: 2,
|
||||
DATE_GRANULARITIES_WITHOUT_GAP_FILLING: new Set([
|
||||
ObjectRecordGroupByDateGranularity.DAY_OF_THE_WEEK,
|
||||
ObjectRecordGroupByDateGranularity.MONTH_OF_THE_YEAR,
|
||||
ObjectRecordGroupByDateGranularity.QUARTER_OF_THE_YEAR,
|
||||
ObjectRecordGroupByDateGranularity.NONE,
|
||||
]),
|
||||
} as const;
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
|
||||
export const BAR_CHART_DATE_GRANULARITIES_WITHOUT_GAP_FILLING = new Set([
|
||||
ObjectRecordGroupByDateGranularity.DAY_OF_THE_WEEK,
|
||||
ObjectRecordGroupByDateGranularity.MONTH_OF_THE_YEAR,
|
||||
ObjectRecordGroupByDateGranularity.QUARTER_OF_THE_YEAR,
|
||||
ObjectRecordGroupByDateGranularity.NONE,
|
||||
]);
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_DEFAULT_INNER_PADDING = 4;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_HOVER_BRIGHTNESS = 0.85;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MAXIMUM_NUMBER_OF_BARS = 100;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MAXIMUM_NUMBER_OF_GROUPS_PER_BAR = 50;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MAXIMUM_VALUE_TICK_COUNT = 6;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MIN_TICK_SPACING_HEIGHT_RATIO = 2.5;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MINIMUM_VALUE_TICK_COUNT = 2;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MINIMUM_WIDTH_PER_TICK = 100;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_OUTER_PADDING_RATIO = 0.05;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_TOOLTIP_OFFSET_PX = 2;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_TOOLTIP_SCROLLABLE_ITEM_THRESHOLD = 5;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const BAR_CHART_MAXIMUM_WIDTH = 32;
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { generateDateGroupsInRange } from '../generateDateGroupsInRange';
|
||||
|
||||
@@ -63,9 +63,11 @@ describe('generateDateGroupsInRange', () => {
|
||||
});
|
||||
|
||||
expect(result.dates.length).toBeLessThanOrEqual(
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS,
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS,
|
||||
);
|
||||
expect(result.dates.length).toBe(
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS,
|
||||
);
|
||||
expect(result.dates.length).toBe(BAR_CHART_MAXIMUM_NUMBER_OF_BARS);
|
||||
expect(result.wasTruncated).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
+8
-7
@@ -1,6 +1,5 @@
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups.constant';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_GROUPS_PER_BAR } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfGroupsPerBar.constant';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import {
|
||||
type BarChartConfiguration,
|
||||
BarChartGroupMode,
|
||||
@@ -16,7 +15,8 @@ describe('getBarChartQueryLimit', () => {
|
||||
} as BarChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS + EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -28,8 +28,8 @@ describe('getBarChartQueryLimit', () => {
|
||||
} as BarChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS *
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_GROUPS_PER_BAR +
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS *
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_GROUPS_PER_BAR +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
@@ -42,7 +42,8 @@ describe('getBarChartQueryLimit', () => {
|
||||
} as BarChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS + EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
import { truncateTickLabel } from '../truncateTickLabel';
|
||||
|
||||
describe('truncateTickLabel', () => {
|
||||
it('should return the original string when it fits within maxLength', () => {
|
||||
expect(truncateTickLabel('Hello', 10)).toBe('Hello');
|
||||
expect(truncateTickLabel('Test', 4)).toBe('Test');
|
||||
});
|
||||
|
||||
it('should truncate and add ellipsis when string exceeds maxLength', () => {
|
||||
expect(truncateTickLabel('Hello World', 8)).toBe('Hello...');
|
||||
expect(truncateTickLabel('LongLabel', 6)).toBe('Lon...');
|
||||
});
|
||||
|
||||
it('should handle numeric values', () => {
|
||||
expect(truncateTickLabel(12345, 4)).toBe('1...');
|
||||
expect(truncateTickLabel(12345, 10)).toBe('12345');
|
||||
});
|
||||
|
||||
it('should return partial ellipsis when maxLength is 3 or less', () => {
|
||||
expect(truncateTickLabel('Hello', 3)).toBe('...');
|
||||
expect(truncateTickLabel('Hello', 2)).toBe('..');
|
||||
expect(truncateTickLabel('Hello', 1)).toBe('.');
|
||||
});
|
||||
|
||||
it('should handle maxLength of 0', () => {
|
||||
expect(truncateTickLabel('Hello', 0)).toBe('');
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
expect(truncateTickLabel('', 5)).toBe('');
|
||||
});
|
||||
|
||||
it('should handle maxLength equal to string length', () => {
|
||||
expect(truncateTickLabel('Hello', 5)).toBe('Hello');
|
||||
});
|
||||
|
||||
it('should handle maxLength of 4 correctly', () => {
|
||||
expect(truncateTickLabel('Hello', 4)).toBe('H...');
|
||||
});
|
||||
});
|
||||
+8
-4
@@ -1,5 +1,4 @@
|
||||
const AVERAGE_CHARACTER_WIDTH_RATIO = 0.6;
|
||||
const MIN_TICK_LABEL_LENGTH = 5;
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
|
||||
export const calculateMaxTickLabelLength = ({
|
||||
widthPerTick,
|
||||
@@ -8,8 +7,13 @@ export const calculateMaxTickLabelLength = ({
|
||||
widthPerTick: number;
|
||||
axisFontSize: number;
|
||||
}): number => {
|
||||
const averageCharacterWidth = axisFontSize * AVERAGE_CHARACTER_WIDTH_RATIO;
|
||||
const averageCharacterWidth =
|
||||
axisFontSize *
|
||||
COMMON_CHART_CONSTANTS.HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO;
|
||||
const calculatedLength = Math.floor(widthPerTick / averageCharacterWidth);
|
||||
|
||||
return Math.max(MIN_TICK_LABEL_LENGTH, calculatedLength);
|
||||
return Math.max(
|
||||
COMMON_CHART_CONSTANTS.MIN_TICK_LABEL_LENGTH,
|
||||
calculatedLength,
|
||||
);
|
||||
};
|
||||
|
||||
+12
-6
@@ -1,4 +1,4 @@
|
||||
import { BAR_CHART_MINIMUM_WIDTH_PER_TICK } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMinimumWidthPerTick';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { computeMinHeightPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeMinHeightPerTick';
|
||||
import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins';
|
||||
@@ -37,14 +37,20 @@ export const computeBarChartCategoryTickValues = ({
|
||||
|
||||
const availableAxisSize = axisSize - totalMargins;
|
||||
|
||||
const minimumSizePerTick =
|
||||
layout === BarChartLayout.VERTICAL
|
||||
? BAR_CHART_MINIMUM_WIDTH_PER_TICK
|
||||
: computeMinHeightPerTick({ axisFontSize });
|
||||
if (layout === BarChartLayout.HORIZONTAL) {
|
||||
return computeChartCategoryTickValues({
|
||||
availableSize: availableAxisSize,
|
||||
minimumSizePerTick: computeMinHeightPerTick({ axisFontSize }),
|
||||
values,
|
||||
});
|
||||
}
|
||||
|
||||
const widthPerTick = data.length > 0 ? availableAxisSize / data.length : 0;
|
||||
|
||||
return computeChartCategoryTickValues({
|
||||
availableSize: availableAxisSize,
|
||||
minimumSizePerTick,
|
||||
minimumSizePerTick: BAR_CHART_CONSTANTS.MINIMUM_WIDTH_PER_TICK_ROTATED,
|
||||
values,
|
||||
widthPerTick,
|
||||
});
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { BAR_CHART_MINIMUM_WIDTH_PER_TICK } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMinimumWidthPerTick';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { computeMinHeightPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeMinHeightPerTick';
|
||||
|
||||
@@ -16,7 +16,7 @@ export const computeBarChartValueTickCount = ({
|
||||
const minTickSize =
|
||||
layout === BarChartLayout.VERTICAL
|
||||
? computeMinHeightPerTick({ axisFontSize })
|
||||
: BAR_CHART_MINIMUM_WIDTH_PER_TICK;
|
||||
: BAR_CHART_CONSTANTS.MINIMUM_WIDTH_PER_TICK;
|
||||
|
||||
return Math.max(1, Math.floor(axisSize / minTickSize));
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
import { BAR_CHART_MIN_TICK_SPACING_HEIGHT_RATIO } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMinTickSpacingHeightRatio';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
|
||||
export const computeMinHeightPerTick = ({
|
||||
axisFontSize,
|
||||
}: {
|
||||
axisFontSize: number;
|
||||
}): number => {
|
||||
return axisFontSize * BAR_CHART_MIN_TICK_SPACING_HEIGHT_RATIO;
|
||||
return axisFontSize * BAR_CHART_CONSTANTS.MIN_TICK_SPACING_HEIGHT_RATIO;
|
||||
};
|
||||
|
||||
+6
-2
@@ -1,4 +1,4 @@
|
||||
import { BAR_CHART_DATE_GRANULARITIES_WITHOUT_GAP_FILLING } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartDateGranularitiesWithoutGapFilling.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { fillDateGapsInOneDimensionalBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/fillDateGapsInOneDimensionalBarChartData';
|
||||
import { fillDateGapsInTwoDimensionalBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/fillDateGapsInTwoDimensionalBarChartData';
|
||||
import { type SupportedDateGranularity } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getDateGroupsFromData';
|
||||
@@ -25,7 +25,11 @@ export const fillDateGapsInBarChartData = ({
|
||||
return { data, wasTruncated: false };
|
||||
}
|
||||
|
||||
if (BAR_CHART_DATE_GRANULARITIES_WITHOUT_GAP_FILLING.has(dateGranularity)) {
|
||||
if (
|
||||
BAR_CHART_CONSTANTS.DATE_GRANULARITIES_WITHOUT_GAP_FILLING.has(
|
||||
dateGranularity,
|
||||
)
|
||||
) {
|
||||
return { data, wasTruncated: false };
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
@@ -31,7 +31,7 @@ export const generateDateGroupsInRange = ({
|
||||
let currentDateCursor = new Date(startDate);
|
||||
|
||||
while (currentDateCursor <= endDate) {
|
||||
if (iterations >= BAR_CHART_MAXIMUM_NUMBER_OF_BARS) {
|
||||
if (iterations >= BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS) {
|
||||
wasTruncated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
+47
-47
@@ -1,51 +1,29 @@
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins';
|
||||
import {
|
||||
type BarChartTickConfig,
|
||||
getBarChartTickConfig,
|
||||
} from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig';
|
||||
import { truncateTickLabel } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/truncateTickLabel';
|
||||
import {
|
||||
formatGraphValue,
|
||||
type GraphValueFormatOptions,
|
||||
} from '@/page-layout/widgets/graph/utils/graphFormatters';
|
||||
import { type BarDatum } from '@nivo/bar';
|
||||
|
||||
const LEFT_AXIS_LEGEND_OFFSET_PADDING = 5;
|
||||
const TICK_PADDING = 5;
|
||||
const BOTTOM_AXIS_LEGEND_OFFSET = 40;
|
||||
|
||||
const COMMON_AXIS_CONFIG = {
|
||||
tickSize: 0,
|
||||
tickPadding: TICK_PADDING,
|
||||
tickRotation: 0,
|
||||
legendPosition: 'middle' as const,
|
||||
};
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { type BarChartTickConfig } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig';
|
||||
|
||||
type GetBarChartAxisConfigsProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
data: BarDatum[];
|
||||
layout: BarChartLayout;
|
||||
indexBy: string;
|
||||
xAxisLabel?: string;
|
||||
yAxisLabel?: string;
|
||||
formatOptions?: GraphValueFormatOptions;
|
||||
axisFontSize?: number;
|
||||
valueTickValues?: number[];
|
||||
tickConfig?: BarChartTickConfig;
|
||||
tickConfig: BarChartTickConfig;
|
||||
};
|
||||
|
||||
export const getBarChartAxisConfigs = ({
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
layout,
|
||||
indexBy,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
formatOptions,
|
||||
axisFontSize = 11,
|
||||
valueTickValues,
|
||||
tickConfig,
|
||||
}: GetBarChartAxisConfigsProps) => {
|
||||
@@ -54,55 +32,72 @@ export const getBarChartAxisConfigs = ({
|
||||
numberOfValueTicks,
|
||||
maxBottomAxisTickLabelLength,
|
||||
maxLeftAxisTickLabelLength,
|
||||
} =
|
||||
tickConfig ??
|
||||
getBarChartTickConfig({
|
||||
width,
|
||||
height,
|
||||
data,
|
||||
indexBy,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
axisFontSize,
|
||||
layout,
|
||||
});
|
||||
bottomAxisTickRotation,
|
||||
} = tickConfig;
|
||||
|
||||
const margins = getBarChartMargins({ xAxisLabel, yAxisLabel, layout });
|
||||
const resolvedValueTickValues =
|
||||
valueTickValues && valueTickValues.length > 0
|
||||
? valueTickValues
|
||||
: numberOfValueTicks;
|
||||
|
||||
const baseMargins = getBarChartMargins({ xAxisLabel, yAxisLabel, layout });
|
||||
|
||||
const hasRotation = bottomAxisTickRotation !== 0;
|
||||
const margins =
|
||||
layout === BarChartLayout.VERTICAL && hasRotation
|
||||
? {
|
||||
...baseMargins,
|
||||
bottom:
|
||||
baseMargins.bottom +
|
||||
COMMON_CHART_CONSTANTS.ROTATED_LABELS_EXTRA_BOTTOM_MARGIN,
|
||||
}
|
||||
: baseMargins;
|
||||
|
||||
if (layout === BarChartLayout.VERTICAL) {
|
||||
return {
|
||||
axisBottom: {
|
||||
...COMMON_AXIS_CONFIG,
|
||||
tickSize: BAR_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: BAR_CHART_CONSTANTS.TICK_PADDING,
|
||||
legendPosition: 'middle' as const,
|
||||
tickValues: categoryTickValues,
|
||||
tickRotation: bottomAxisTickRotation,
|
||||
legend: xAxisLabel,
|
||||
legendOffset: BOTTOM_AXIS_LEGEND_OFFSET,
|
||||
legendOffset:
|
||||
BAR_CHART_CONSTANTS.BOTTOM_AXIS_LEGEND_OFFSET +
|
||||
(hasRotation
|
||||
? BAR_CHART_CONSTANTS.ROTATED_LABELS_EXTRA_BOTTOM_MARGIN
|
||||
: 0),
|
||||
format: (value: string | number) =>
|
||||
truncateTickLabel(String(value), maxBottomAxisTickLabelLength),
|
||||
},
|
||||
axisLeft: {
|
||||
...COMMON_AXIS_CONFIG,
|
||||
tickSize: BAR_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: BAR_CHART_CONSTANTS.TICK_PADDING,
|
||||
legendPosition: 'middle' as const,
|
||||
tickRotation: COMMON_CHART_CONSTANTS.NO_ROTATION_ANGLE,
|
||||
tickValues: resolvedValueTickValues,
|
||||
legend: yAxisLabel,
|
||||
legendOffset: -margins.left + LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
legendOffset:
|
||||
-margins.left + BAR_CHART_CONSTANTS.LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
format: (value: number) =>
|
||||
truncateTickLabel(
|
||||
formatGraphValue(value, formatOptions ?? {}),
|
||||
maxLeftAxisTickLabelLength,
|
||||
),
|
||||
},
|
||||
margins,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
axisBottom: {
|
||||
...COMMON_AXIS_CONFIG,
|
||||
tickSize: BAR_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: BAR_CHART_CONSTANTS.TICK_PADDING,
|
||||
legendPosition: 'middle' as const,
|
||||
tickRotation: BAR_CHART_CONSTANTS.NO_ROTATION_ANGLE,
|
||||
tickValues: resolvedValueTickValues,
|
||||
legend: yAxisLabel,
|
||||
legendOffset: BOTTOM_AXIS_LEGEND_OFFSET,
|
||||
legendOffset: BAR_CHART_CONSTANTS.BOTTOM_AXIS_LEGEND_OFFSET,
|
||||
format: (value: number) =>
|
||||
truncateTickLabel(
|
||||
formatGraphValue(value, formatOptions ?? {}),
|
||||
@@ -110,12 +105,17 @@ export const getBarChartAxisConfigs = ({
|
||||
),
|
||||
},
|
||||
axisLeft: {
|
||||
...COMMON_AXIS_CONFIG,
|
||||
tickSize: COMMON_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: COMMON_CHART_CONSTANTS.TICK_PADDING,
|
||||
legendPosition: 'middle' as const,
|
||||
tickRotation: COMMON_CHART_CONSTANTS.NO_ROTATION_ANGLE,
|
||||
tickValues: categoryTickValues,
|
||||
legend: xAxisLabel,
|
||||
legendOffset: -margins.left + LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
legendOffset:
|
||||
-margins.left + BAR_CHART_CONSTANTS.LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
format: (value: string | number) =>
|
||||
truncateTickLabel(String(value), maxLeftAxisTickLabelLength),
|
||||
},
|
||||
margins,
|
||||
};
|
||||
};
|
||||
|
||||
+14
-9
@@ -1,5 +1,4 @@
|
||||
import { BAR_CHART_DEFAULT_INNER_PADDING } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartDefaultInnerPadding';
|
||||
import { BAR_CHART_OUTER_PADDING_RATIO } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartOuterPaddingRatio';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
|
||||
type BarChartMargins = {
|
||||
@@ -19,8 +18,6 @@ type GetBarChartInnerPaddingProps = {
|
||||
groupMode?: 'grouped' | 'stacked';
|
||||
};
|
||||
|
||||
const MINIMUM_BAR_WIDTH = 2;
|
||||
|
||||
export const getBarChartInnerPadding = ({
|
||||
chartWidth,
|
||||
chartHeight,
|
||||
@@ -35,7 +32,7 @@ export const getBarChartInnerPadding = ({
|
||||
}
|
||||
|
||||
if (dataLength === 0 || keysLength === 0) {
|
||||
return BAR_CHART_DEFAULT_INNER_PADDING;
|
||||
return BAR_CHART_CONSTANTS.DEFAULT_INNER_PADDING;
|
||||
}
|
||||
|
||||
const availableSpace =
|
||||
@@ -44,13 +41,21 @@ export const getBarChartInnerPadding = ({
|
||||
: chartHeight - margins.top - margins.bottom;
|
||||
|
||||
const spacePerGroup =
|
||||
(availableSpace / dataLength) * (1 - BAR_CHART_OUTER_PADDING_RATIO);
|
||||
(availableSpace / dataLength) *
|
||||
(1 - BAR_CHART_CONSTANTS.OUTER_PADDING_RATIO);
|
||||
|
||||
const spacePerBar = spacePerGroup / keysLength;
|
||||
|
||||
if (spacePerBar < MINIMUM_BAR_WIDTH + BAR_CHART_DEFAULT_INNER_PADDING) {
|
||||
return Math.max(0, (spacePerBar - MINIMUM_BAR_WIDTH) / 2);
|
||||
if (
|
||||
spacePerBar <
|
||||
BAR_CHART_CONSTANTS.MINIMUM_BAR_WIDTH +
|
||||
BAR_CHART_CONSTANTS.DEFAULT_INNER_PADDING
|
||||
) {
|
||||
return Math.max(
|
||||
0,
|
||||
(spacePerBar - BAR_CHART_CONSTANTS.MINIMUM_BAR_WIDTH) / 2,
|
||||
);
|
||||
}
|
||||
|
||||
return BAR_CHART_DEFAULT_INNER_PADDING;
|
||||
return BAR_CHART_CONSTANTS.DEFAULT_INNER_PADDING;
|
||||
};
|
||||
|
||||
+17
-16
@@ -1,32 +1,33 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const BAR_CHART_MARGINS = {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 45,
|
||||
left: 65,
|
||||
top: COMMON_CHART_CONSTANTS.MARGIN_TOP,
|
||||
right: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
bottom: COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITHOUT_LABEL,
|
||||
left: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITHOUT_LABEL,
|
||||
} as const;
|
||||
|
||||
const BAR_CHART_MARGINS_WITH_BOTH_LABELS = {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 60,
|
||||
left: 80,
|
||||
top: COMMON_CHART_CONSTANTS.MARGIN_TOP,
|
||||
right: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
bottom: COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITH_LABEL,
|
||||
left: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITH_LABEL,
|
||||
} as const;
|
||||
|
||||
const BAR_CHART_MARGINS_WITH_X_LABEL = {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 60,
|
||||
left: 65,
|
||||
top: COMMON_CHART_CONSTANTS.MARGIN_TOP,
|
||||
right: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
bottom: COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITH_LABEL,
|
||||
left: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITHOUT_LABEL,
|
||||
} as const;
|
||||
|
||||
const BAR_CHART_MARGINS_WITH_Y_LABEL = {
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 45,
|
||||
left: 80,
|
||||
top: COMMON_CHART_CONSTANTS.MARGIN_TOP,
|
||||
right: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
bottom: COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITHOUT_LABEL,
|
||||
left: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITH_LABEL,
|
||||
} as const;
|
||||
|
||||
export const getBarChartMargins = ({
|
||||
|
||||
+6
-6
@@ -1,6 +1,5 @@
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups.constant';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_GROUPS_PER_BAR } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfGroupsPerBar.constant';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { isChartConfigurationTwoDimensional } from '@/page-layout/widgets/graph/utils/isChartConfigurationTwoDimensional';
|
||||
import {
|
||||
BarChartGroupMode,
|
||||
@@ -15,13 +14,14 @@ export const getBarChartQueryLimit = (
|
||||
configuration.groupMode === BarChartGroupMode.STACKED
|
||||
) {
|
||||
return (
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS *
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_GROUPS_PER_BAR +
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS *
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_GROUPS_PER_BAR +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS + EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
);
|
||||
};
|
||||
|
||||
+22
-12
@@ -1,20 +1,19 @@
|
||||
import { BAR_CHART_MAXIMUM_VALUE_TICK_COUNT } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumValueTickCount';
|
||||
import { BAR_CHART_MINIMUM_VALUE_TICK_COUNT } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMinimumValueTickCount';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { calculateMaxTickLabelLength } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateMaxTickLabelLength';
|
||||
import { calculateWidthPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick';
|
||||
import { computeBarChartCategoryTickValues } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues';
|
||||
import { computeBarChartValueTickCount } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount';
|
||||
import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins';
|
||||
import { computeMaxLabelLengthForMargin } from '@/page-layout/widgets/graph/utils/computeMaxLabelLengthForMargin';
|
||||
import { getTickRotationConfig } from '@/page-layout/widgets/graph/utils/getTickRotationConfig';
|
||||
import { type BarDatum } from '@nivo/bar';
|
||||
|
||||
const MAX_LEFT_AXIS_LABEL_LENGTH = 10;
|
||||
|
||||
export type BarChartTickConfig = {
|
||||
categoryTickValues: (string | number)[];
|
||||
numberOfValueTicks: number;
|
||||
maxBottomAxisTickLabelLength: number;
|
||||
maxLeftAxisTickLabelLength: number;
|
||||
bottomAxisTickRotation: number;
|
||||
};
|
||||
|
||||
export const getBarChartTickConfig = ({
|
||||
@@ -38,8 +37,8 @@ export const getBarChartTickConfig = ({
|
||||
}): BarChartTickConfig => {
|
||||
const clampValueTickCount = (tickCount: number) =>
|
||||
Math.min(
|
||||
BAR_CHART_MAXIMUM_VALUE_TICK_COUNT,
|
||||
Math.max(BAR_CHART_MINIMUM_VALUE_TICK_COUNT, tickCount),
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_VALUE_TICK_COUNT,
|
||||
Math.max(BAR_CHART_CONSTANTS.MINIMUM_VALUE_TICK_COUNT, tickCount),
|
||||
);
|
||||
|
||||
const categoryTickValues = computeBarChartCategoryTickValues({
|
||||
@@ -73,18 +72,29 @@ export const getBarChartTickConfig = ({
|
||||
valueTickCount: numberOfValueTicks,
|
||||
});
|
||||
|
||||
const maxBottomAxisTickLabelLength = calculateMaxTickLabelLength({
|
||||
widthPerTick,
|
||||
const actualDataPointCount = data.length;
|
||||
const widthPerDataPoint =
|
||||
layout === BarChartLayout.VERTICAL &&
|
||||
actualDataPointCount > 0 &&
|
||||
availableWidth > 0
|
||||
? availableWidth / actualDataPointCount
|
||||
: widthPerTick;
|
||||
|
||||
const tickRotationConfig = getTickRotationConfig({
|
||||
widthPerTick: widthPerDataPoint,
|
||||
axisFontSize,
|
||||
});
|
||||
|
||||
// TODO: Make this dynamic based on the data
|
||||
const maxLeftAxisTickLabelLength = MAX_LEFT_AXIS_LABEL_LENGTH;
|
||||
const maxLeftAxisTickLabelLength = computeMaxLabelLengthForMargin({
|
||||
marginSize: margins.left,
|
||||
axisFontSize,
|
||||
});
|
||||
|
||||
return {
|
||||
categoryTickValues,
|
||||
numberOfValueTicks,
|
||||
maxBottomAxisTickLabelLength,
|
||||
maxBottomAxisTickLabelLength: tickRotationConfig.maxLabelLength,
|
||||
maxLeftAxisTickLabelLength,
|
||||
bottomAxisTickRotation: tickRotationConfig.tickRotation,
|
||||
};
|
||||
};
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI
|
||||
import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { getGroupByQueryResultGqlFieldName } from '@/page-layout/utils/getGroupByQueryResultGqlFieldName';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout';
|
||||
import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries';
|
||||
import { fillDateGapsInBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/fillDateGapsInBarChartData';
|
||||
|
||||
+7
-3
@@ -2,7 +2,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { GRAPH_DEFAULT_COLOR } from '@/page-layout/widgets/graph/constants/GraphDefaultColor.constant';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries';
|
||||
import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
import { type GroupByRawResult } from '@/page-layout/widgets/graph/types/GroupByRawResult';
|
||||
@@ -56,7 +56,10 @@ export const transformOneDimensionalGroupByToBarChartData = ({
|
||||
: aggregateField.name;
|
||||
|
||||
// TODO: Add a limit to the query instead of slicing here (issue: twentyhq/core-team-issues#1600)
|
||||
const limitedResults = rawResults.slice(0, BAR_CHART_MAXIMUM_NUMBER_OF_BARS);
|
||||
const limitedResults = rawResults.slice(
|
||||
0,
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS,
|
||||
);
|
||||
|
||||
const formattedValues = formatPrimaryDimensionValues({
|
||||
groupByRawResults: limitedResults,
|
||||
@@ -119,7 +122,8 @@ export const transformOneDimensionalGroupByToBarChartData = ({
|
||||
indexBy: indexByKey,
|
||||
keys: [aggregateValueKey],
|
||||
series,
|
||||
hasTooManyGroups: rawResults.length > BAR_CHART_MAXIMUM_NUMBER_OF_BARS,
|
||||
hasTooManyGroups:
|
||||
rawResults.length > BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS,
|
||||
formattedToRawLookup,
|
||||
};
|
||||
};
|
||||
|
||||
+6
-3
@@ -1,7 +1,7 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants';
|
||||
import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries';
|
||||
import { sortBarChartDataBySecondaryDimensionSum } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/sortBarChartDataBySecondaryDimensionSum';
|
||||
import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
@@ -97,7 +97,10 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
|
||||
const isNewY = !yValues.has(yValue);
|
||||
|
||||
if (configuration.groupMode === BarChartGroupMode.STACKED) {
|
||||
if (isNewX && xValues.size >= BAR_CHART_MAXIMUM_NUMBER_OF_BARS) {
|
||||
if (
|
||||
isNewX &&
|
||||
xValues.size >= BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS
|
||||
) {
|
||||
hasTooManyGroups = true;
|
||||
return;
|
||||
}
|
||||
@@ -110,7 +113,7 @@ export const transformTwoDimensionalGroupByToBarChartData = ({
|
||||
|
||||
if (
|
||||
totalUniqueDimensions + additionalDimensions >
|
||||
BAR_CHART_MAXIMUM_NUMBER_OF_BARS
|
||||
BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS
|
||||
) {
|
||||
hasTooManyGroups = true;
|
||||
return;
|
||||
|
||||
+10
-2
@@ -1,10 +1,18 @@
|
||||
import { ELLIPSIS_LENGTH } from '@/page-layout/widgets/graph/utils/ellipsisLength';
|
||||
|
||||
export const truncateTickLabel = (
|
||||
value: string | number,
|
||||
maxLength: number,
|
||||
): string => {
|
||||
const stringValue = String(value);
|
||||
if (maxLength < 4 || stringValue.length <= maxLength) {
|
||||
|
||||
if (stringValue.length <= maxLength) {
|
||||
return stringValue;
|
||||
}
|
||||
return `${stringValue.slice(0, maxLength - 3)}...`;
|
||||
|
||||
if (maxLength <= ELLIPSIS_LENGTH) {
|
||||
return '...'.slice(0, maxLength);
|
||||
}
|
||||
|
||||
return `${stringValue.slice(0, maxLength - ELLIPSIS_LENGTH)}...`;
|
||||
};
|
||||
|
||||
+13
-15
@@ -1,10 +1,4 @@
|
||||
import { LINE_CHART_CROSSHAIR_DASH_ARRAY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartCrosshairDashArray';
|
||||
import { LINE_CHART_CROSSHAIR_STROKE_OPACITY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartCrosshairStrokeOpacity';
|
||||
import { LINE_CHART_CROSSHAIR_STROKE_WIDTH } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartCrosshairStrokeWidth';
|
||||
import { LINE_CHART_CROSSHAIR_TRANSITION_DAMPING } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartCrosshairTransitionDamping';
|
||||
import { LINE_CHART_CROSSHAIR_TRANSITION_STIFFNESS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartCrosshairTransitionStiffness';
|
||||
import { LINE_CHART_MARGIN_LEFT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginLeft';
|
||||
import { LINE_CHART_MARGIN_TOP } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginTop';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { graphWidgetLineCrosshairXComponentState } from '@/page-layout/widgets/graph/graphWidgetLineChart/states/graphWidgetLineCrosshairXComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useTheme } from '@emotion/react';
|
||||
@@ -29,6 +23,8 @@ type CustomCrosshairLayerProps = {
|
||||
points: readonly Point<LineSeries>[];
|
||||
innerHeight: number;
|
||||
innerWidth: number;
|
||||
marginLeft: number;
|
||||
marginTop: number;
|
||||
onSliceHover: (data: SliceHoverData) => void;
|
||||
onSliceClick?: (data: SliceHoverData) => void;
|
||||
onRectLeave: (relatedTarget: EventTarget | null) => void;
|
||||
@@ -38,6 +34,8 @@ export const CustomCrosshairLayer = ({
|
||||
points,
|
||||
innerHeight,
|
||||
innerWidth,
|
||||
marginLeft,
|
||||
marginTop,
|
||||
onSliceHover,
|
||||
onSliceClick,
|
||||
onRectLeave,
|
||||
@@ -75,8 +73,8 @@ export const CustomCrosshairLayer = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const mouseX = event.clientX - svgRect.left - LINE_CHART_MARGIN_LEFT;
|
||||
const mouseY = event.clientY - svgRect.top - LINE_CHART_MARGIN_TOP;
|
||||
const mouseX = event.clientX - svgRect.left - marginLeft;
|
||||
const mouseY = event.clientY - svgRect.top - marginTop;
|
||||
|
||||
const nearestSlice = slices.reduce((nearest, slice) => {
|
||||
const currentDistance = Math.abs(slice.x - mouseX);
|
||||
@@ -102,7 +100,7 @@ export const CustomCrosshairLayer = ({
|
||||
svgRect,
|
||||
};
|
||||
},
|
||||
[slices],
|
||||
[marginLeft, marginTop, slices],
|
||||
);
|
||||
|
||||
const handleMouseMove = (event: MouseEvent<SVGRectElement>) => {
|
||||
@@ -136,8 +134,8 @@ export const CustomCrosshairLayer = ({
|
||||
|
||||
const transition = {
|
||||
type: 'spring',
|
||||
stiffness: LINE_CHART_CROSSHAIR_TRANSITION_STIFFNESS,
|
||||
damping: LINE_CHART_CROSSHAIR_TRANSITION_DAMPING,
|
||||
stiffness: LINE_CHART_CONSTANTS.CROSSHAIR_TRANSITION_STIFFNESS,
|
||||
damping: LINE_CHART_CONSTANTS.CROSSHAIR_TRANSITION_DAMPING,
|
||||
} as const;
|
||||
|
||||
return (
|
||||
@@ -149,9 +147,9 @@ export const CustomCrosshairLayer = ({
|
||||
y1={0}
|
||||
y2={innerHeight}
|
||||
stroke={theme.font.color.primary}
|
||||
strokeWidth={LINE_CHART_CROSSHAIR_STROKE_WIDTH}
|
||||
strokeOpacity={LINE_CHART_CROSSHAIR_STROKE_OPACITY}
|
||||
strokeDasharray={LINE_CHART_CROSSHAIR_DASH_ARRAY}
|
||||
strokeWidth={LINE_CHART_CONSTANTS.CROSSHAIR_STROKE_WIDTH}
|
||||
strokeOpacity={LINE_CHART_CONSTANTS.CROSSHAIR_STROKE_OPACITY}
|
||||
strokeDasharray={LINE_CHART_CONSTANTS.CROSSHAIR_DASH_ARRAY}
|
||||
initial={{ x1: crosshairX, x2: crosshairX, opacity: 0 }}
|
||||
animate={{ x1: crosshairX, x2: crosshairX, opacity: 0.5 }}
|
||||
exit={{ opacity: 0 }}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { GraphWidgetFloatingTooltip } from '@/page-layout/widgets/graph/components/GraphWidgetFloatingTooltip';
|
||||
import { LINE_CHART_TOOLTIP_OFFSET_PX } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartTooltipOffsetPx';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { graphWidgetLineTooltipComponentState } from '@/page-layout/widgets/graph/graphWidgetLineChart/states/graphWidgetLineTooltipComponentState';
|
||||
import { type LineChartEnrichedSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartEnrichedSeries';
|
||||
import { getLineChartTooltipData } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/getLineChartTooltipData';
|
||||
@@ -71,7 +71,7 @@ export const GraphLineChartTooltip = ({
|
||||
<GraphWidgetFloatingTooltip
|
||||
reference={reference}
|
||||
boundary={containerElement}
|
||||
tooltipOffsetFromAnchorInPx={LINE_CHART_TOOLTIP_OFFSET_PX}
|
||||
tooltipOffsetFromAnchorInPx={LINE_CHART_CONSTANTS.TOOLTIP_OFFSET_PX}
|
||||
items={tooltipData?.items ?? []}
|
||||
indexLabel={tooltipData?.indexLabel}
|
||||
highlightedKey={tooltipState?.highlightedSeriesId}
|
||||
|
||||
+18
-25
@@ -1,6 +1,7 @@
|
||||
import { GraphWidgetChartContainer } from '@/page-layout/widgets/graph/components/GraphWidgetChartContainer';
|
||||
import { GraphWidgetLegend } from '@/page-layout/widgets/graph/components/GraphWidgetLegend';
|
||||
import { NoDataLayer } from '@/page-layout/widgets/graph/components/NoDataLayer';
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import {
|
||||
CustomCrosshairLayer,
|
||||
type SliceHoverData,
|
||||
@@ -9,10 +10,7 @@ import { CustomLinesLayer } from '@/page-layout/widgets/graph/graphWidgetLineCha
|
||||
import { CustomPointLabelsLayer } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/CustomPointLabelsLayer';
|
||||
import { CustomStackedAreasLayer } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/CustomStackedAreasLayer';
|
||||
import { GraphLineChartTooltip } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/GraphLineChartTooltip';
|
||||
import { LINE_CHART_MARGIN_BOTTOM } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginBottom';
|
||||
import { LINE_CHART_MARGIN_LEFT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginLeft';
|
||||
import { LINE_CHART_MARGIN_RIGHT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginRight';
|
||||
import { LINE_CHART_MARGIN_TOP } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginTop';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { useLineChartData } from '@/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartData';
|
||||
import { useLineChartTheme } from '@/page-layout/widgets/graph/graphWidgetLineChart/hooks/useLineChartTheme';
|
||||
import { graphWidgetLineCrosshairXComponentState } from '@/page-layout/widgets/graph/graphWidgetLineChart/states/graphWidgetLineCrosshairXComponentState';
|
||||
@@ -49,8 +47,6 @@ type StackedAreasLayerProps = LineCustomSvgLayerProps<LineSeries>;
|
||||
type LinesLayerProps = LineCustomSvgLayerProps<LineSeries>;
|
||||
type NoDataLayerWrapperProps = LineCustomSvgLayerProps<LineSeries>;
|
||||
|
||||
const LINE_CHART_DEFAULT_TICK_COUNT = 5;
|
||||
|
||||
type GraphWidgetLineChartProps = {
|
||||
data: LineChartSeries[];
|
||||
showLegend?: boolean;
|
||||
@@ -171,8 +167,8 @@ export const GraphWidgetLineChart = ({
|
||||
points: sliceData.nearestSlice.points,
|
||||
};
|
||||
|
||||
const offsetLeft = sliceData.nearestSlice.x + LINE_CHART_MARGIN_LEFT;
|
||||
const offsetTop = sliceData.mouseY + LINE_CHART_MARGIN_TOP;
|
||||
const offsetLeft = sliceData.nearestSlice.x + marginLeft;
|
||||
const offsetTop = sliceData.mouseY + COMMON_CHART_CONSTANTS.MARGIN_TOP;
|
||||
|
||||
debouncedHideTooltip.cancel();
|
||||
setCrosshairX(sliceData.sliceX);
|
||||
@@ -212,6 +208,8 @@ export const GraphWidgetLineChart = ({
|
||||
points={layerProps.points}
|
||||
innerHeight={layerProps.innerHeight}
|
||||
innerWidth={layerProps.innerWidth}
|
||||
marginLeft={marginLeft}
|
||||
marginTop={COMMON_CHART_CONSTANTS.MARGIN_TOP}
|
||||
onSliceHover={handleSliceEnter}
|
||||
onSliceClick={
|
||||
isDefined(onSliceClick)
|
||||
@@ -262,29 +260,24 @@ export const GraphWidgetLineChart = ({
|
||||
/>
|
||||
);
|
||||
|
||||
const axisBottomConfig = getLineChartAxisBottomConfig(
|
||||
xAxisLabel,
|
||||
chartWidth,
|
||||
visibleData,
|
||||
);
|
||||
const chartMargins = {
|
||||
top: LINE_CHART_MARGIN_TOP,
|
||||
right: LINE_CHART_MARGIN_RIGHT,
|
||||
bottom: LINE_CHART_MARGIN_BOTTOM,
|
||||
left: LINE_CHART_MARGIN_LEFT,
|
||||
};
|
||||
const marginLeft = isDefined(yAxisLabel)
|
||||
? COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITH_LABEL
|
||||
: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITHOUT_LABEL;
|
||||
|
||||
const { config: axisBottomConfig, marginBottom } =
|
||||
getLineChartAxisBottomConfig(xAxisLabel, chartWidth, data, marginLeft);
|
||||
|
||||
const { tickValues: valueTickValues, domain: valueDomain } =
|
||||
computeValueTickValues({
|
||||
minimum: effectiveMinimumValue,
|
||||
maximum: effectiveMaximumValue,
|
||||
tickCount: LINE_CHART_DEFAULT_TICK_COUNT,
|
||||
tickCount: LINE_CHART_CONSTANTS.DEFAULT_TICK_COUNT,
|
||||
});
|
||||
|
||||
const axisLeftConfig = getLineChartAxisLeftConfig(
|
||||
yAxisLabel,
|
||||
formatOptions,
|
||||
valueTickValues,
|
||||
chartMargins.left,
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -303,10 +296,10 @@ export const GraphWidgetLineChart = ({
|
||||
<ResponsiveLine
|
||||
data={nivoData}
|
||||
margin={{
|
||||
top: chartMargins.top,
|
||||
right: chartMargins.right,
|
||||
bottom: chartMargins.bottom,
|
||||
left: chartMargins.left,
|
||||
top: COMMON_CHART_CONSTANTS.MARGIN_TOP,
|
||||
right: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
bottom: marginBottom,
|
||||
left: marginLeft,
|
||||
}}
|
||||
xScale={{ type: 'point' }}
|
||||
yScale={{
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPag
|
||||
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
|
||||
import { ChartSkeletonLoader } from '@/page-layout/widgets/graph/components/ChartSkeletonLoader';
|
||||
import { GraphWidgetChartHasTooManyGroupsEffect } from '@/page-layout/widgets/graph/components/GraphWidgetChartHasTooManyGroupsEffect';
|
||||
import { LINE_CHART_IS_STACKED_DEFAULT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartIsStackedDefault';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { useGraphLineChartWidgetData } from '@/page-layout/widgets/graph/graphWidgetLineChart/hooks/useGraphLineChartWidgetData';
|
||||
import { type LineChartDataPoint } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartDataPoint';
|
||||
import { assertLineChartWidgetOrThrow } from '@/page-layout/widgets/graph/utils/assertLineChartWidget';
|
||||
@@ -59,7 +59,7 @@ export const GraphWidgetLineChartRenderer = ({
|
||||
|
||||
const groupMode =
|
||||
hasGroupByOnSecondaryAxis &&
|
||||
(configuration.isStacked ?? LINE_CHART_IS_STACKED_DEFAULT)
|
||||
(configuration.isStacked ?? LINE_CHART_CONSTANTS.IS_STACKED_DEFAULT)
|
||||
? 'stacked'
|
||||
: undefined;
|
||||
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_AREA_FILL_END_OPACITY = 0.01;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_AREA_FILL_START_OPACITY = 0.18;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
|
||||
export const LINE_CHART_CONSTANTS = {
|
||||
...COMMON_CHART_CONSTANTS,
|
||||
MAXIMUM_NUMBER_OF_DATA_POINTS: 100,
|
||||
MAXIMUM_NUMBER_OF_NON_STACKED_SERIES: 50,
|
||||
MAXIMUM_NUMBER_OF_STACKED_SERIES: 50,
|
||||
TOOLTIP_OFFSET_PX: 4,
|
||||
IS_STACKED_DEFAULT: true,
|
||||
AREA_FILL_START_OPACITY: 0.18,
|
||||
AREA_FILL_END_OPACITY: 0.01,
|
||||
CROSSHAIR_STROKE_WIDTH: 1,
|
||||
CROSSHAIR_STROKE_OPACITY: 0.5,
|
||||
CROSSHAIR_DASH_ARRAY: '4 4',
|
||||
CROSSHAIR_TRANSITION_DAMPING: 30,
|
||||
CROSSHAIR_TRANSITION_STIFFNESS: 500,
|
||||
DEFAULT_TICK_COUNT: 5,
|
||||
} as const;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_CROSSHAIR_DASH_ARRAY = '4 4';
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_CROSSHAIR_STROKE_OPACITY = 0.5;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_CROSSHAIR_STROKE_WIDTH = 1;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_CROSSHAIR_TRANSITION_DAMPING = 30;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_CROSSHAIR_TRANSITION_STIFFNESS = 500;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_IS_STACKED_DEFAULT = true;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MARGIN_BOTTOM = 60;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MARGIN_LEFT = 80;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MARGIN_RIGHT = 20;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MARGIN_TOP = 20;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS = 100;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MAXIMUM_NUMBER_OF_NON_STACKED_SERIES = 50;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_MAXIMUM_NUMBER_OF_STACKED_SERIES = 50;
|
||||
-1
@@ -1 +0,0 @@
|
||||
export const LINE_CHART_TOOLTIP_OFFSET_PX = 4;
|
||||
+7
-9
@@ -1,7 +1,5 @@
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_NON_STACKED_SERIES } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfNonStackedSeries.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_STACKED_SERIES } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfStackedSeries.constant';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { type LineChartConfiguration } from '~/generated-metadata/graphql';
|
||||
import { getLineChartQueryLimit } from '../getLineChartQueryLimit';
|
||||
|
||||
@@ -14,7 +12,7 @@ describe('getLineChartQueryLimit', () => {
|
||||
} as LineChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
@@ -27,8 +25,8 @@ describe('getLineChartQueryLimit', () => {
|
||||
} as LineChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_STACKED_SERIES +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_STACKED_SERIES +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
@@ -41,8 +39,8 @@ describe('getLineChartQueryLimit', () => {
|
||||
} as LineChartConfiguration);
|
||||
|
||||
expect(result).toBe(
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_NON_STACKED_SERIES +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_NON_STACKED_SERIES +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS,
|
||||
);
|
||||
});
|
||||
|
||||
+11
-7
@@ -1,16 +1,17 @@
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries';
|
||||
import { computeChartCategoryTickValues } from '@/page-layout/widgets/graph/utils/computeChartCategoryTickValues';
|
||||
|
||||
const LINE_CHART_MARGIN_LEFT = 70;
|
||||
const LINE_CHART_MARGIN_RIGHT = 20;
|
||||
const LINE_CHART_MINIMUM_WIDTH_PER_TICK = 100;
|
||||
|
||||
export const computeLineChartCategoryTickValues = ({
|
||||
width,
|
||||
data,
|
||||
marginLeft,
|
||||
marginRight,
|
||||
}: {
|
||||
width: number;
|
||||
data: LineChartSeries[];
|
||||
marginLeft: number;
|
||||
marginRight: number;
|
||||
}): (string | number)[] => {
|
||||
if (data.length === 0 || data[0].data.length === 0) {
|
||||
return [];
|
||||
@@ -23,12 +24,15 @@ export const computeLineChartCategoryTickValues = ({
|
||||
: String(value);
|
||||
});
|
||||
|
||||
const availableWidth =
|
||||
width - (LINE_CHART_MARGIN_LEFT + LINE_CHART_MARGIN_RIGHT);
|
||||
const availableWidth = width - (marginLeft + marginRight);
|
||||
|
||||
const dataPointCount = data[0].data.length;
|
||||
const widthPerTick = dataPointCount > 0 ? availableWidth / dataPointCount : 0;
|
||||
|
||||
return computeChartCategoryTickValues({
|
||||
availableSize: availableWidth,
|
||||
minimumSizePerTick: LINE_CHART_MINIMUM_WIDTH_PER_TICK,
|
||||
minimumSizePerTick: LINE_CHART_CONSTANTS.MINIMUM_WIDTH_PER_TICK_ROTATED,
|
||||
values,
|
||||
widthPerTick,
|
||||
});
|
||||
};
|
||||
|
||||
+3
-4
@@ -1,5 +1,4 @@
|
||||
import { LINE_AREA_FILL_END_OPACITY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillEndOpacity';
|
||||
import { LINE_AREA_FILL_START_OPACITY } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineAreaFillStartOpacity';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { type GraphColorScheme } from '@/page-layout/widgets/graph/types/GraphColorScheme';
|
||||
|
||||
export const createAreaFillDef = (
|
||||
@@ -17,12 +16,12 @@ export const createAreaFillDef = (
|
||||
{
|
||||
offset: 0,
|
||||
color: colorScheme.solid,
|
||||
opacity: LINE_AREA_FILL_START_OPACITY,
|
||||
opacity: LINE_CHART_CONSTANTS.AREA_FILL_START_OPACITY,
|
||||
},
|
||||
{
|
||||
offset: 100,
|
||||
color: colorScheme.solid,
|
||||
opacity: LINE_AREA_FILL_END_OPACITY,
|
||||
opacity: LINE_CHART_CONSTANTS.AREA_FILL_END_OPACITY,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
+73
-9
@@ -1,23 +1,87 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { truncateTickLabel } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/truncateTickLabel';
|
||||
import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries';
|
||||
import { computeLineChartCategoryTickValues } from '@/page-layout/widgets/graph/graphWidgetLineChart/utils/computeLineChartCategoryTickValues';
|
||||
import { getTickRotationConfig } from '@/page-layout/widgets/graph/utils/getTickRotationConfig';
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export type LineChartAxisBottomResult = {
|
||||
config: {
|
||||
tickSize: number;
|
||||
tickPadding: number;
|
||||
tickRotation: number;
|
||||
tickValues: (string | number)[] | undefined;
|
||||
legend: string | undefined;
|
||||
legendPosition: 'middle';
|
||||
legendOffset: number;
|
||||
format: (value: string | number) => string;
|
||||
};
|
||||
marginBottom: number;
|
||||
};
|
||||
|
||||
export const getLineChartAxisBottomConfig = (
|
||||
xAxisLabel?: string,
|
||||
width?: number,
|
||||
data?: LineChartSeries[],
|
||||
) => {
|
||||
marginLeft?: number,
|
||||
): LineChartAxisBottomResult => {
|
||||
const effectiveMarginLeft =
|
||||
marginLeft ?? COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITH_LABEL;
|
||||
|
||||
const tickValues =
|
||||
width && data
|
||||
? computeLineChartCategoryTickValues({ width, data })
|
||||
? computeLineChartCategoryTickValues({
|
||||
width,
|
||||
data,
|
||||
marginLeft: effectiveMarginLeft,
|
||||
marginRight: COMMON_CHART_CONSTANTS.MARGIN_RIGHT,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
const availableWidth = width
|
||||
? width - (effectiveMarginLeft + COMMON_CHART_CONSTANTS.MARGIN_RIGHT)
|
||||
: 0;
|
||||
|
||||
const actualDataPointCount = isNonEmptyArray(data?.[0]?.data)
|
||||
? data[0].data.length
|
||||
: 1;
|
||||
|
||||
const widthPerDataPoint =
|
||||
actualDataPointCount > 0 && availableWidth > 0
|
||||
? availableWidth / actualDataPointCount
|
||||
: 0;
|
||||
|
||||
const { tickRotation, maxLabelLength } = getTickRotationConfig({
|
||||
widthPerTick: widthPerDataPoint,
|
||||
axisFontSize: COMMON_CHART_CONSTANTS.AXIS_FONT_SIZE,
|
||||
});
|
||||
|
||||
const hasRotation = tickRotation !== 0;
|
||||
const baseMarginBottom = isDefined(xAxisLabel)
|
||||
? COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITH_LABEL
|
||||
: COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITHOUT_LABEL;
|
||||
const marginBottom = hasRotation
|
||||
? baseMarginBottom +
|
||||
COMMON_CHART_CONSTANTS.ROTATED_LABELS_EXTRA_BOTTOM_MARGIN
|
||||
: baseMarginBottom;
|
||||
|
||||
return {
|
||||
tickSize: 0,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
tickValues,
|
||||
legend: xAxisLabel,
|
||||
legendPosition: 'middle' as const,
|
||||
legendOffset: 40,
|
||||
config: {
|
||||
tickSize: COMMON_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: COMMON_CHART_CONSTANTS.TICK_PADDING,
|
||||
tickRotation,
|
||||
tickValues,
|
||||
legend: xAxisLabel,
|
||||
legendPosition: 'middle' as const,
|
||||
legendOffset:
|
||||
COMMON_CHART_CONSTANTS.BOTTOM_AXIS_LEGEND_OFFSET +
|
||||
(hasRotation
|
||||
? COMMON_CHART_CONSTANTS.ROTATED_LABELS_EXTRA_BOTTOM_MARGIN
|
||||
: 0),
|
||||
format: (value: string | number) =>
|
||||
truncateTickLabel(String(value), maxLabelLength),
|
||||
},
|
||||
marginBottom,
|
||||
};
|
||||
};
|
||||
|
||||
+23
-15
@@ -1,24 +1,32 @@
|
||||
import { LINE_CHART_MARGIN_LEFT } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMarginLeft';
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import {
|
||||
formatGraphValue,
|
||||
type GraphValueFormatOptions,
|
||||
} from '@/page-layout/widgets/graph/utils/graphFormatters';
|
||||
|
||||
const LEFT_AXIS_LEGEND_OFFSET_PADDING = 5;
|
||||
const TICK_PADDING = 5;
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const getLineChartAxisLeftConfig = (
|
||||
yAxisLabel?: string,
|
||||
formatOptions?: GraphValueFormatOptions,
|
||||
tickValues?: number[],
|
||||
marginLeft: number = LINE_CHART_MARGIN_LEFT,
|
||||
) => ({
|
||||
tickSize: 0,
|
||||
tickPadding: TICK_PADDING,
|
||||
tickRotation: 0,
|
||||
tickValues,
|
||||
legend: yAxisLabel,
|
||||
legendPosition: 'middle' as const,
|
||||
legendOffset: -marginLeft + LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
format: (value: number) => formatGraphValue(value, formatOptions || {}),
|
||||
});
|
||||
marginLeft?: number,
|
||||
) => {
|
||||
const effectiveMarginLeft =
|
||||
marginLeft ??
|
||||
(isDefined(yAxisLabel)
|
||||
? COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITH_LABEL
|
||||
: COMMON_CHART_CONSTANTS.MARGIN_LEFT_WITHOUT_LABEL);
|
||||
|
||||
return {
|
||||
tickSize: COMMON_CHART_CONSTANTS.TICK_SIZE,
|
||||
tickPadding: COMMON_CHART_CONSTANTS.TICK_PADDING,
|
||||
tickRotation: COMMON_CHART_CONSTANTS.NO_ROTATION_ANGLE,
|
||||
tickValues,
|
||||
legend: yAxisLabel,
|
||||
legendPosition: 'middle' as const,
|
||||
legendOffset:
|
||||
-effectiveMarginLeft +
|
||||
COMMON_CHART_CONSTANTS.LEFT_AXIS_LEGEND_OFFSET_PADDING,
|
||||
format: (value: number) => formatGraphValue(value, formatOptions || {}),
|
||||
};
|
||||
};
|
||||
|
||||
+7
-9
@@ -1,7 +1,5 @@
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_NON_STACKED_SERIES } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfNonStackedSeries.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_STACKED_SERIES } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfStackedSeries.constant';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { isChartConfigurationTwoDimensional } from '@/page-layout/widgets/graph/utils/isChartConfigurationTwoDimensional';
|
||||
import { type LineChartConfiguration } from '~/generated/graphql';
|
||||
|
||||
@@ -12,22 +10,22 @@ export const getLineChartQueryLimit = (
|
||||
|
||||
if (!isTwoDimensional) {
|
||||
return (
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
);
|
||||
}
|
||||
|
||||
if (configuration.isStacked === true) {
|
||||
return (
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_STACKED_SERIES +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_STACKED_SERIES +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_NON_STACKED_SERIES +
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS *
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_NON_STACKED_SERIES +
|
||||
EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS
|
||||
);
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups.constant';
|
||||
import { EXTRA_ITEM_TO_DETECT_TOO_MANY_GROUPS } from '@/page-layout/widgets/graph/constants/ExtraItemToDetectTooManyGroups';
|
||||
import { PIE_CHART_MAXIMUM_NUMBER_OF_SLICES } from '@/page-layout/widgets/graph/graphWidgetPieChart/constants/PieChartMaximumNumberOfSlices.constant';
|
||||
import { type PieChartDataItem } from '@/page-layout/widgets/graph/graphWidgetPieChart/types/PieChartDataItem';
|
||||
import { transformGroupByDataToPieChartData } from '@/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData';
|
||||
|
||||
+57
@@ -1,3 +1,4 @@
|
||||
import { ROTATION_THRESHOLD_WIDTH } from '@/page-layout/widgets/graph/constants/RotationThresholdWidth';
|
||||
import { computeChartCategoryTickValues } from '../computeChartCategoryTickValues';
|
||||
|
||||
describe('computeChartCategoryTickValues', () => {
|
||||
@@ -89,4 +90,60 @@ describe('computeChartCategoryTickValues', () => {
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
describe('with widthPerTick parameter', () => {
|
||||
it('should return all values when widthPerTick is above rotation threshold', () => {
|
||||
const values = ['A', 'B', 'C', 'D', 'E'];
|
||||
const result = computeChartCategoryTickValues({
|
||||
availableSize: 200,
|
||||
minimumSizePerTick: 100,
|
||||
values,
|
||||
widthPerTick: ROTATION_THRESHOLD_WIDTH + 10,
|
||||
});
|
||||
|
||||
expect(result).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
});
|
||||
|
||||
it('should return all values when rotated and widthPerTick exceeds minimumSizePerTick', () => {
|
||||
const values = ['A', 'B', 'C', 'D', 'E'];
|
||||
const result = computeChartCategoryTickValues({
|
||||
availableSize: 200,
|
||||
minimumSizePerTick: 20,
|
||||
values,
|
||||
widthPerTick: 30,
|
||||
});
|
||||
|
||||
expect(result).toEqual(['A', 'B', 'C', 'D', 'E']);
|
||||
});
|
||||
|
||||
it('should omit ticks when rotated and widthPerTick is below minimumSizePerTick', () => {
|
||||
const values = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
||||
const result = computeChartCategoryTickValues({
|
||||
availableSize: 400,
|
||||
minimumSizePerTick: 100,
|
||||
values,
|
||||
widthPerTick: 10,
|
||||
});
|
||||
|
||||
expect(result.length).toBe(4);
|
||||
expect(result).toEqual(['A', 'C', 'F', 'H']);
|
||||
});
|
||||
|
||||
it('should behave like no widthPerTick when widthPerTick is undefined', () => {
|
||||
const values = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];
|
||||
const resultWithoutWidthPerTick = computeChartCategoryTickValues({
|
||||
availableSize: 400,
|
||||
minimumSizePerTick: 100,
|
||||
values,
|
||||
});
|
||||
const resultWithUndefined = computeChartCategoryTickValues({
|
||||
availableSize: 400,
|
||||
minimumSizePerTick: 100,
|
||||
values,
|
||||
widthPerTick: undefined,
|
||||
});
|
||||
|
||||
expect(resultWithoutWidthPerTick).toEqual(resultWithUndefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { computeMaxLabelLengthForMargin } from '../computeMaxLabelLengthForMargin';
|
||||
|
||||
describe('computeMaxLabelLengthForMargin', () => {
|
||||
it('should calculate label length based on margin size and font size', () => {
|
||||
const result = computeMaxLabelLengthForMargin({
|
||||
marginSize: 100,
|
||||
axisFontSize: 11,
|
||||
});
|
||||
|
||||
const expectedCharacterWidth =
|
||||
11 * COMMON_CHART_CONSTANTS.HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO;
|
||||
const availableWidth = 100 - COMMON_CHART_CONSTANTS.TICK_PADDING_ALLOWANCE;
|
||||
const expectedLength = Math.floor(availableWidth / expectedCharacterWidth);
|
||||
|
||||
expect(result).toBe(expectedLength);
|
||||
});
|
||||
|
||||
it('should return minimum length when margin is too small', () => {
|
||||
const result = computeMaxLabelLengthForMargin({
|
||||
marginSize: 20,
|
||||
axisFontSize: 11,
|
||||
});
|
||||
|
||||
const minimumLength =
|
||||
COMMON_CHART_CONSTANTS.TICK_MINIMUM_NUMBER_OF_DISPLAYED_CHARACTERS;
|
||||
|
||||
expect(result).toBe(minimumLength);
|
||||
});
|
||||
|
||||
it('should scale with font size', () => {
|
||||
const smallFont = computeMaxLabelLengthForMargin({
|
||||
marginSize: 100,
|
||||
axisFontSize: 10,
|
||||
});
|
||||
|
||||
const largeFont = computeMaxLabelLengthForMargin({
|
||||
marginSize: 100,
|
||||
axisFontSize: 14,
|
||||
});
|
||||
|
||||
expect(smallFont).toBeGreaterThan(largeFont);
|
||||
});
|
||||
|
||||
it('should scale with margin size', () => {
|
||||
const smallMargin = computeMaxLabelLengthForMargin({
|
||||
marginSize: 50,
|
||||
axisFontSize: 11,
|
||||
});
|
||||
|
||||
const largeMargin = computeMaxLabelLengthForMargin({
|
||||
marginSize: 150,
|
||||
axisFontSize: 11,
|
||||
});
|
||||
|
||||
expect(largeMargin).toBeGreaterThan(smallMargin);
|
||||
});
|
||||
|
||||
it('should never return less than the minimum displayable characters', () => {
|
||||
const result = computeMaxLabelLengthForMargin({
|
||||
marginSize: 0,
|
||||
axisFontSize: 11,
|
||||
});
|
||||
|
||||
const minimumLength =
|
||||
COMMON_CHART_CONSTANTS.TICK_MINIMUM_NUMBER_OF_DISPLAYED_CHARACTERS;
|
||||
|
||||
expect(result).toBeGreaterThanOrEqual(minimumLength);
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
|
||||
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { CalendarStartDay } from 'twenty-shared';
|
||||
import {
|
||||
type FirstDayOfTheWeek,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { type AggregateOperations } from '@/object-record/record-table/constants
|
||||
import { COUNT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/countAggregateOperationOptions';
|
||||
import { PERCENT_AGGREGATE_OPERATION_OPTIONS } from '@/object-record/record-table/record-table-footer/constants/percentAggregateOperationOptions';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { GRAPH_DEFAULT_AGGREGATE_VALUE } from '@/page-layout/widgets/graph/constants/GraphDefaultAggregateValue.constant';
|
||||
import { GRAPH_DEFAULT_AGGREGATE_VALUE } from '@/page-layout/widgets/graph/constants/GraphDefaultAggregateValue';
|
||||
import isEmpty from 'lodash.isempty';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
+13
@@ -1,18 +1,31 @@
|
||||
import { ROTATION_THRESHOLD_WIDTH } from '@/page-layout/widgets/graph/constants/RotationThresholdWidth';
|
||||
import { computeCategoryTickValues } from '@/page-layout/widgets/graph/utils/computeCategoryTickValues';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const computeChartCategoryTickValues = ({
|
||||
availableSize,
|
||||
minimumSizePerTick,
|
||||
values,
|
||||
widthPerTick,
|
||||
}: {
|
||||
availableSize: number;
|
||||
minimumSizePerTick: number;
|
||||
values: (string | number)[];
|
||||
widthPerTick?: number;
|
||||
}): (string | number)[] => {
|
||||
if (availableSize <= 0 || values.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isDefined(widthPerTick)) {
|
||||
const willRotate = widthPerTick < ROTATION_THRESHOLD_WIDTH;
|
||||
const shouldOmitTicks = willRotate && widthPerTick < minimumSizePerTick;
|
||||
|
||||
if (!shouldOmitTicks) {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
const numberOfTicks = Math.floor(availableSize / minimumSizePerTick);
|
||||
const tickIndices = computeCategoryTickValues(numberOfTicks, values.length);
|
||||
|
||||
|
||||
+6
-6
@@ -1,8 +1,6 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const POSITIVE_RANGE_PADDING_RATIO = 0.1;
|
||||
const MINIMUM_POSITIVE_RANGE_PADDING = 1;
|
||||
|
||||
type ComputeEffectiveValueRangeParams = {
|
||||
calculatedMinimum: number;
|
||||
calculatedMaximum: number;
|
||||
@@ -39,8 +37,9 @@ export const computeEffectiveValueRange = ({
|
||||
? positiveRangePaddingTarget
|
||||
: positiveRangePaddingTarget +
|
||||
Math.max(
|
||||
Math.abs(positiveRangePaddingTarget) * POSITIVE_RANGE_PADDING_RATIO,
|
||||
MINIMUM_POSITIVE_RANGE_PADDING,
|
||||
Math.abs(positiveRangePaddingTarget) *
|
||||
COMMON_CHART_CONSTANTS.POSITIVE_RANGE_PADDING_RATIO,
|
||||
COMMON_CHART_CONSTANTS.MINIMUM_POSITIVE_RANGE_PADDING,
|
||||
);
|
||||
|
||||
let effectiveMinimumValue = baseMinimumValue;
|
||||
@@ -49,7 +48,8 @@ export const computeEffectiveValueRange = ({
|
||||
if (!isDefined(rangeMax) && !isDefined(rangeMin)) {
|
||||
if (effectiveMinimumValue === effectiveMaximumValue) {
|
||||
effectiveMaximumValue =
|
||||
effectiveMinimumValue + MINIMUM_POSITIVE_RANGE_PADDING;
|
||||
effectiveMinimumValue +
|
||||
COMMON_CHART_CONSTANTS.MINIMUM_POSITIVE_RANGE_PADDING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
|
||||
export const computeMaxLabelLengthForMargin = ({
|
||||
marginSize,
|
||||
axisFontSize,
|
||||
}: {
|
||||
marginSize: number;
|
||||
axisFontSize: number;
|
||||
}): number => {
|
||||
const characterWidth =
|
||||
axisFontSize *
|
||||
COMMON_CHART_CONSTANTS.HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO;
|
||||
const availableWidth =
|
||||
marginSize - COMMON_CHART_CONSTANTS.TICK_PADDING_ALLOWANCE;
|
||||
const calculatedLength = Math.max(
|
||||
COMMON_CHART_CONSTANTS.TICK_MINIMUM_NUMBER_OF_DISPLAYED_CHARACTERS,
|
||||
Math.floor(availableWidth / characterWidth),
|
||||
);
|
||||
|
||||
return calculatedLength;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export const ELLIPSIS_LENGTH = 3;
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { formatDateByGranularity } from '@/page-layout/widgets/graph/utils/formatDateByGranularity';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { GRAPH_DEFAULT_ORDER_BY } from '@/page-layout/widgets/graph/constants/GraphDefaultOrderBy';
|
||||
import { getGroupByOrderBy } from '@/page-layout/widgets/graph/utils/getGroupByOrderBy';
|
||||
import { isRelationNestedFieldDateKind } from '@/page-layout/widgets/graph/utils/isRelationNestedFieldDateKind';
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { getGroupByOrderBy } from '@/page-layout/widgets/graph/utils/getGroupByOrderBy';
|
||||
import { isRelationNestedFieldDateKind } from '@/page-layout/widgets/graph/utils/isRelationNestedFieldDateKind';
|
||||
import {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType';
|
||||
import { isFieldMorphRelation } from '@/object-record/record-field/ui/types/guards/isFieldMorphRelation';
|
||||
import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import { getRelationFieldOrderBy } from '@/page-layout/widgets/graph/utils/getRelationFieldOrderBy';
|
||||
import {
|
||||
type ObjectRecordGroupByDateGranularity,
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity.constant';
|
||||
import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity';
|
||||
import {
|
||||
type ObjectRecordGroupByDateGranularity,
|
||||
type ObjectRecordOrderByForRelationField,
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants';
|
||||
|
||||
const TICK_ROTATION_ANGLE_RAD =
|
||||
(Math.abs(COMMON_CHART_CONSTANTS.TICK_ROTATION_ANGLE) * Math.PI) / 180;
|
||||
const MIN_CALCULATED_LENGTH = 1;
|
||||
const TICK_MARGIN = 1;
|
||||
|
||||
export type TickRotationConfig = {
|
||||
tickRotation: number;
|
||||
maxLabelLength: number;
|
||||
};
|
||||
|
||||
export const getTickRotationConfig = ({
|
||||
widthPerTick,
|
||||
axisFontSize,
|
||||
}: {
|
||||
widthPerTick: number;
|
||||
axisFontSize: number;
|
||||
}): TickRotationConfig => {
|
||||
const shouldRotate =
|
||||
widthPerTick <
|
||||
COMMON_CHART_CONSTANTS.TICK_MINIMUM_NUMBER_OF_DISPLAYED_CHARACTERS *
|
||||
COMMON_CHART_CONSTANTS.HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO *
|
||||
axisFontSize +
|
||||
TICK_MARGIN;
|
||||
|
||||
if (shouldRotate) {
|
||||
const characterWidth =
|
||||
axisFontSize * COMMON_CHART_CONSTANTS.ROTATED_LABEL_CHARACTER_WIDTH_RATIO;
|
||||
const calculatedLength = Math.max(
|
||||
MIN_CALCULATED_LENGTH,
|
||||
Math.floor(
|
||||
COMMON_CHART_CONSTANTS.MARGIN_BOTTOM_WITHOUT_LABEL /
|
||||
(characterWidth * Math.sin(TICK_ROTATION_ANGLE_RAD)),
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
tickRotation: COMMON_CHART_CONSTANTS.TICK_ROTATION_ANGLE,
|
||||
maxLabelLength: calculatedLength,
|
||||
};
|
||||
}
|
||||
|
||||
const characterWidth =
|
||||
axisFontSize *
|
||||
COMMON_CHART_CONSTANTS.HORIZONTAL_LABEL_CHARACTER_WIDTH_RATIO;
|
||||
|
||||
const calculatedLength = Math.max(
|
||||
MIN_CALCULATED_LENGTH,
|
||||
Math.floor(widthPerTick / characterWidth),
|
||||
);
|
||||
|
||||
return {
|
||||
tickRotation: COMMON_CHART_CONSTANTS.NO_ROTATION_ANGLE,
|
||||
maxLabelLength: calculatedLength,
|
||||
};
|
||||
};
|
||||
+3
-3
@@ -2,7 +2,7 @@ import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataIte
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { GRAPH_DEFAULT_COLOR } from '@/page-layout/widgets/graph/constants/GraphDefaultColor.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { type LineChartDataPoint } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartDataPoint';
|
||||
import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries';
|
||||
import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
@@ -44,7 +44,7 @@ export const transformOneDimensionalGroupByToLineChartData = ({
|
||||
// TODO: Add a limit to the query instead of slicing here (issue: twentyhq/core-team-issues#1600)
|
||||
const limitedResults = rawResults.slice(
|
||||
0,
|
||||
LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS,
|
||||
LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS,
|
||||
);
|
||||
|
||||
const formattedValues = formatPrimaryDimensionValues({
|
||||
@@ -112,7 +112,7 @@ export const transformOneDimensionalGroupByToLineChartData = ({
|
||||
return {
|
||||
series,
|
||||
hasTooManyGroups:
|
||||
rawResults.length > LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS,
|
||||
rawResults.length > LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS,
|
||||
formattedToRawLookup,
|
||||
};
|
||||
};
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { LINE_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartConstants';
|
||||
import { type LineChartDataPoint } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartDataPoint';
|
||||
import { type LineChartSeries } from '@/page-layout/widgets/graph/graphWidgetLineChart/types/LineChartSeries';
|
||||
import { type GraphColor } from '@/page-layout/widgets/graph/types/GraphColor';
|
||||
@@ -73,7 +73,10 @@ export const transformTwoDimensionalGroupByToLineChartData = ({
|
||||
// TODO: Add a limit to the query instead of checking here (issue: twentyhq/core-team-issues#1600)
|
||||
const isNewX = !xValueSet.has(xValue);
|
||||
|
||||
if (isNewX && xValueSet.size >= LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS) {
|
||||
if (
|
||||
isNewX &&
|
||||
xValueSet.size >= LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS
|
||||
) {
|
||||
hasTooManyGroups = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user