@evergis/react 4.0.101 → 4.0.102

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -154,7 +154,7 @@ export interface CameraContainerConfig extends Omit<ConfigContainerChild, "optio
154
154
  export interface CameraContainerProps extends Omit<ContainerProps, "elementConfig"> {
155
155
  elementConfig?: CameraContainerConfig;
156
156
  }
157
- export type ChartContainerOptions = Pick<ConfigOptions, "twoColumns" | "hideEmpty" | "width" | "height" | "chartFill">;
157
+ export type ChartContainerOptions = Pick<ConfigOptions, "twoColumns" | "hideEmpty" | "width" | "height" | "fill">;
158
158
  export interface ChartAliasChild extends Omit<ConfigContainerChild, "id"> {
159
159
  id: "alias";
160
160
  }
@@ -0,0 +1,15 @@
1
+ /** Значение контекста вписывания тела графика в контейнер (`options.fill` контейнера Chart). */
2
+ export interface FillContextValue {
3
+ /** Вписать тело графика в контейнер: по ширине всегда, по высоте — при заданной высоте (object-fit: contain). */
4
+ fill: boolean;
5
+ /** У контейнера ограниченная высота ⇒ можно фитить по высоте (иначе только по ширине). */
6
+ fitHeight: boolean;
7
+ }
8
+ /**
9
+ * Контекст вписывания графика (`options.fill` контейнера Chart).
10
+ *
11
+ * Компонент `Chart` рендерится через `renderElement({ id: "chart" })` и получает только дочерний
12
+ * узел `{ id: "chart" }` — опции контейнера до него не доходят. `ChartContainer` оборачивает
13
+ * график в провайдер этого контекста, а `Chart` читает значение через `useContext`.
14
+ */
15
+ export declare const FillContext: import('react').Context<FillContextValue>;
@@ -15,6 +15,7 @@ export declare const PieChartCenter: import('styled-components').StyledComponent
15
15
  export declare const ChartWrapperContainer: import('styled-components').StyledComponent<"div", any, {
16
16
  column: boolean;
17
17
  }, never>;
18
+ export declare const ChartFillMeasure: import('styled-components').StyledComponent<"div", any, {}, never>;
18
19
  export declare const Tooltip: import('styled-components').StyledComponent<"div", any, {
19
20
  transform?: CSSProperties["transform"];
20
21
  }, never>;
@@ -2,9 +2,11 @@ export declare const CheckboxWrapper: import('styled-components').StyledComponen
2
2
  fontColor?: string;
3
3
  big?: boolean;
4
4
  column?: boolean;
5
+ $fill?: boolean;
5
6
  }, never>;
6
7
  export declare const ChipsWrapper: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps & {
7
8
  fontColor?: string;
8
9
  big?: boolean;
9
10
  column?: boolean;
11
+ $fill?: boolean;
10
12
  }, never>;
@@ -2,13 +2,16 @@ export declare const ContainerAlias: import('styled-components').StyledComponent
2
2
  hasBottomMargin?: boolean;
3
3
  }, never>;
4
4
  export declare const ContainerAliasIcon: import('styled-components').StyledComponent<"div", any, {}, never>;
5
- export declare const ContainerChart: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps, never>;
5
+ export declare const ContainerChart: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps & {
6
+ $fill?: boolean;
7
+ }, never>;
6
8
  export declare const ContainerLegend: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps, never>;
7
9
  export declare const ContainerUnits: import('styled-components').StyledComponent<"div", any, {}, never>;
8
10
  export declare const ContainerValue: import('styled-components').StyledComponent<"div", any, import('@evergis/uilib-gl').FlexProps & {
9
11
  fontColor?: string;
10
12
  big?: boolean;
11
13
  column?: boolean;
14
+ $fill?: boolean;
12
15
  }, never>;
13
16
  export declare const ContainerIcon: import('styled-components').StyledComponent<"span", any, import('@evergis/uilib-gl').IIconProps & {
14
17
  $fontColor?: string;
@@ -25,7 +25,7 @@ export * from './useHideIfEmptyDataSource';
25
25
  export * from './useProjectDashboardInit';
26
26
  export * from './useRelatedDataSourceAttributes';
27
27
  export * from './useRenderElement';
28
- export * from './useResizeWidth';
28
+ export * from './useResizeBox';
29
29
  export * from './useShownOtherItems';
30
30
  export * from './useUpdateDataSource';
31
31
  export * from './useWidgetConfig';
@@ -0,0 +1,17 @@
1
+ import { RefObject } from 'react';
2
+ /** Измеренный content-box наблюдаемого элемента: обе оси в пикселях либо `undefined` до первого замера. */
3
+ export interface ResizeBox {
4
+ width?: number;
5
+ height?: number;
6
+ }
7
+ /**
8
+ * Измеряет фактические ширину и высоту DOM-элемента и обновляет их на любой ресайз (ResizeObserver).
9
+ *
10
+ * Возвращает `[ref, box]`, где `box` — `{ width, height }` в пикселях (content-box) либо пустой объект,
11
+ * пока элемент ещё не измерен. Используется для «резинового» графика: d3-графикам нужны конкретные
12
+ * пиксельные размеры, а не CSS `100%`, а для вписывания (fit) нужны обе оси.
13
+ *
14
+ * `enabled: false` полностью отключает наблюдение — обычные (не fill) графики не измеряются
15
+ * и не вызывают лишних ре-рендеров.
16
+ */
17
+ export declare const useResizeBox: (enabled: boolean) => [RefObject<HTMLDivElement>, ResizeBox];
@@ -162,8 +162,8 @@ export interface ConfigLayoutOptions {
162
162
  cornerRadius?: number;
163
163
  column?: boolean;
164
164
  twoColumns?: boolean;
165
- /** Растянуть график контейнера Chart на всю ширину контейнера (график любого типа). */
166
- chartFill?: boolean;
165
+ /** Вписать график контейнера Chart в контейнер: по ширине всегда, по высоте — при заданной высоте (object-fit: contain). */
166
+ fill?: boolean;
167
167
  /** Горизонтальное выравнивание содержимого заголовка контейнера. */
168
168
  align?: Alignment;
169
169
  /** Выравнивание детей контейнера по поперечной оси. В ряду по умолчанию `center`. */
@@ -187,6 +187,10 @@ export interface MarkdownTagTypography {
187
187
  fontSize?: FontSizeToken | string;
188
188
  lineHeight?: string;
189
189
  fontWeight?: number | string;
190
+ /** Отступ снизу — задаёт расстояние до следующего блока (между тегами). */
191
+ marginBottom?: string;
192
+ /** Отступ сверху — задаёт расстояние до предыдущего блока (между тегами). */
193
+ marginTop?: string;
190
194
  }
191
195
  /**
192
196
  * Пер-тег типографика markdown-виджета. Отсутствующий тег или свойство —
package/dist/index.js CHANGED
@@ -102,7 +102,30 @@ const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapper
102
102
  position: relative;
103
103
  width: 100%;
104
104
  `;
105
- const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-2prpr" }) `
105
+ /*
106
+ * fill-режим: обёртка забирает всю доступную ячейку (её высоту задаёт флекс-цепочка, а не контент графика),
107
+ * на неё вешается ref для ResizeObserver — меряем обе оси без цикла. Плоский div (не Flex uilib), как соседний
108
+ * ChartWrapperContainer, чтобы ref гарантированно дошёл до DOM-узла. Тело графика центрируется внутри.
109
+ */
110
+ const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure", componentId: "sc-1r4oerb" }) `
111
+ position: relative;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ width: 100%;
116
+ height: 100%;
117
+ min-width: 0;
118
+ min-height: 0;
119
+ overflow: hidden;
120
+
121
+ /* SVG по умолчанию inline, поэтому под ним остаётся зазор базовой линии (~4px при line-height
122
+ 18.4px): вне fill его скрывал запас высоты, а тело, точно заполнившее ячейку, из-за него
123
+ вылезает за нижнюю границу. Блочный SVG зазора не даёт. */
124
+ svg {
125
+ display: block;
126
+ }
127
+ `;
128
+ const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-1va8cy3" }) `
106
129
  position: relative;
107
130
  border-radius: 0.25rem;
108
131
  background-color: rgba(28, 33, 48);
@@ -195,7 +218,7 @@ const LineChartStyles = styled.createGlobalStyle `
195
218
  fill: ${({ theme: { palette } }) => palette.textDisabled};
196
219
  }
197
220
  `;
198
- const StyledBarChart = styled(charts.BarChart).withConfig({ displayName: "StyledBarChart", componentId: "sc-1i0hrvl" }) `
221
+ const StyledBarChart = styled(charts.BarChart).withConfig({ displayName: "StyledBarChart", componentId: "sc-1l1kx80" }) `
199
222
  .domain {
200
223
  display: none;
201
224
  }
@@ -7632,30 +7655,33 @@ const useRenderElement = (type = exports.WidgetType.Dashboard, elementConfig) =>
7632
7655
  ]);
7633
7656
  };
7634
7657
 
7658
+ /** Пустой бокс на отключённом пути — стабильная ссылка, чтобы не плодить ре-рендеры. */
7659
+ const EMPTY_BOX = {};
7635
7660
  /**
7636
- * Измеряет фактическую ширину DOM-элемента и обновляет её на любой ресайз (ResizeObserver).
7661
+ * Измеряет фактические ширину и высоту DOM-элемента и обновляет их на любой ресайз (ResizeObserver).
7637
7662
  *
7638
- * Возвращает `[ref, width]`, где `width` — число пикселей либо `undefined`, пока элемент ещё
7639
- * не измерен. Используется для «резинового» графика: d3-графикам нужна конкретная пиксельная
7640
- * ширина, а не CSS `100%`.
7663
+ * Возвращает `[ref, box]`, где `box` — `{ width, height }` в пикселях (content-box) либо пустой объект,
7664
+ * пока элемент ещё не измерен. Используется для «резинового» графика: d3-графикам нужны конкретные
7665
+ * пиксельные размеры, а не CSS `100%`, а для вписывания (fit) нужны обе оси.
7641
7666
  *
7642
7667
  * `enabled: false` полностью отключает наблюдение — обычные (не fill) графики не измеряются
7643
7668
  * и не вызывают лишних ре-рендеров.
7644
7669
  */
7645
- const useResizeWidth = (enabled) => {
7670
+ const useResizeBox = (enabled) => {
7646
7671
  const ref = React.useRef(null);
7647
- const [width, setWidth] = React.useState();
7672
+ const [box, setBox] = React.useState(EMPTY_BOX);
7648
7673
  React.useEffect(() => {
7649
7674
  const node = ref.current;
7650
7675
  if (!enabled || !node)
7651
7676
  return;
7652
7677
  const observer = new ResizeObserver(([entry]) => {
7653
- setWidth(entry.contentRect.width);
7678
+ const { width, height } = entry.contentRect;
7679
+ setBox({ width, height });
7654
7680
  });
7655
7681
  observer.observe(node);
7656
7682
  return () => observer.disconnect();
7657
7683
  }, [enabled]);
7658
- return [ref, enabled ? width : undefined];
7684
+ return [ref, enabled ? box : EMPTY_BOX];
7659
7685
  };
7660
7686
 
7661
7687
  const useShownOtherItems = (options) => {
@@ -7777,6 +7803,19 @@ const ContainerChart = styled(uilibGl.Flex).withConfig({ displayName: "Container
7777
7803
  justify-content: center;
7778
7804
  width: 100%;
7779
7805
  }
7806
+
7807
+ /* fill: слот забирает остаток высоты ячейки (под слотами title/alias/legend) и центрирует тело графика. */
7808
+ ${({ $fill }) => $fill &&
7809
+ styled.css `
7810
+ flex: 1 1 auto;
7811
+ min-height: 0;
7812
+ align-items: center;
7813
+ justify-content: center;
7814
+
7815
+ > * {
7816
+ height: 100%;
7817
+ }
7818
+ `}
7780
7819
  `;
7781
7820
  const ContainerLegend = styled(uilibGl.Flex).withConfig({ displayName: "ContainerLegend", componentId: "sc-z1n1s8" }) ``;
7782
7821
  const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-1vurgy9" }) `
@@ -7792,6 +7831,13 @@ const ContainerValue = styled(uilibGl.Flex).withConfig({ displayName: "Container
7792
7831
  font-size: ${({ big }) => (big ? 1.5 : 1)}rem;
7793
7832
  color: ${({ fontColor, theme: { palette } }) => fontColor || palette.textPrimary};
7794
7833
 
7834
+ /* fill: строка со слотом chart+legend забирает остаток высоты Container'а (гейт — только у Chart-контейнера). */
7835
+ ${({ $fill }) => $fill &&
7836
+ styled.css `
7837
+ flex: 1 1 auto;
7838
+ min-height: 0;
7839
+ `}
7840
+
7795
7841
  > * {
7796
7842
  width: ${({ column }) => (column ? "100%" : "auto")};
7797
7843
  }
@@ -8423,6 +8469,10 @@ const ChartContainerWrapper = styled(uilibGl.FlexSpan).withConfig({ displayName:
8423
8469
  /* Заданная высота контейнера достаётся телу графика — за вычетом заголовка. */
8424
8470
  ${({ $sizeCss }) => !!$sizeCss?.height &&
8425
8471
  styled.css `
8472
+ /* Раздача высоты требует, чтобы обёртка была flex-колонкой (FlexSpan сам по себе не flex),
8473
+ иначе Container с flex:1 1 auto не растянется на заданную высоту, а fill-график не впишется по высоте. */
8474
+ display: flex;
8475
+
8426
8476
  > ${Container} {
8427
8477
  flex: 1 1 auto;
8428
8478
  min-height: 0;
@@ -8433,17 +8483,19 @@ const ChartContainerWrapper = styled(uilibGl.FlexSpan).withConfig({ displayName:
8433
8483
  `;
8434
8484
 
8435
8485
  /**
8436
- * Флаг «растянуть график на всю ширину контейнера» (`options.chartFill` контейнера Chart).
8486
+ * Контекст вписывания графика (`options.fill` контейнера Chart).
8437
8487
  *
8438
8488
  * Компонент `Chart` рендерится через `renderElement({ id: "chart" })` и получает только дочерний
8439
8489
  * узел `{ id: "chart" }` — опции контейнера до него не доходят. `ChartContainer` оборачивает
8440
- * график в провайдер этого контекста, а `Chart` читает флаг через `useContext`.
8490
+ * график в провайдер этого контекста, а `Chart` читает значение через `useContext`.
8441
8491
  */
8442
- const ChartFillContext = React.createContext(false);
8492
+ const FillContext = React.createContext({ fill: false, fitHeight: false });
8443
8493
 
8444
8494
  const ChartContainer = React.memo(({ elementConfig, isVisible, type, renderElement }) => {
8445
8495
  const { options, children } = elementConfig || {};
8446
- const { twoColumns, hideEmpty, chartFill } = options || {};
8496
+ const { twoColumns, hideEmpty, height } = options || {};
8497
+ const fill = options?.fill;
8498
+ const fillContextValue = React.useMemo(() => ({ fill: !!fill, fitHeight: !!fill && height != null }), [fill, height]);
8447
8499
  const aliasElement = children.find(child => child.id === "alias");
8448
8500
  const chartElement = children.find(child => child.id === "chart");
8449
8501
  const legendElement = children.find(child => child.id === "legend");
@@ -8458,7 +8510,7 @@ const ChartContainer = React.memo(({ elementConfig, isVisible, type, renderEleme
8458
8510
  const hasItems = !!data[0]?.items?.length;
8459
8511
  if (!loading && !hasItems && hideEmpty)
8460
8512
  return null;
8461
- return (jsxRuntime.jsxs(ChartContainerWrapper, { ...root, children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { isColumn: true, children: [aliasElement && jsxRuntime.jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsxRuntime.jsx(ContainerValue, { column: !twoColumns, alignItems: "center", children: hasItems ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ContainerChart, { children: jsxRuntime.jsx(ChartFillContext.Provider, { value: !!chartFill, children: renderElement({ id: "chart" }) }) }), jsxRuntime.jsx(ContainerLegend, { justifyContent: legendElement?.options?.center ? "center" : "flex-start", children: renderElement({ id: "legend" }) })] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2014" })) })] }))] }));
8513
+ return (jsxRuntime.jsxs(ChartContainerWrapper, { ...root, children: [jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { isColumn: true, children: [aliasElement && jsxRuntime.jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsxRuntime.jsx(ContainerValue, { column: !twoColumns, alignItems: twoColumns && fill ? "stretch" : "center", "$fill": !!fill, children: hasItems ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(ContainerChart, { "$fill": !!fill, children: jsxRuntime.jsx(FillContext.Provider, { value: fillContextValue, children: renderElement({ id: "chart" }) }) }), jsxRuntime.jsx(ContainerLegend, { justifyContent: legendElement?.options?.center ? "center" : "flex-start", children: renderElement({ id: "legend" }) })] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2014" })) })] }))] }));
8462
8514
  });
8463
8515
 
8464
8516
  const PagesContainer = React.memo(({ type = exports.WidgetType.Dashboard, noBorders }) => {
@@ -11465,6 +11517,8 @@ const tagTypography = (tag) => ({ $typography }) => {
11465
11517
  ${style.fontSize && `font-size: ${style.fontSize};`}
11466
11518
  ${style.lineHeight && `line-height: ${style.lineHeight};`}
11467
11519
  ${style.fontWeight != null && `font-weight: ${style.fontWeight};`}
11520
+ ${style.marginTop && `margin-top: ${style.marginTop};`}
11521
+ ${style.marginBottom && `margin-bottom: ${style.marginBottom};`}
11468
11522
  `;
11469
11523
  };
11470
11524
  const MarkdownWrapper = styled.div.withConfig({ displayName: "MarkdownWrapper", componentId: "sc-1iv8z8a" }) `
@@ -12697,7 +12751,8 @@ const ChartLoading = ({ column }) => (jsxRuntime.jsx(uilibGl.Flex, { position: "
12697
12751
 
12698
12752
  const ContainerLoading = () => (jsxRuntime.jsx(uilibGl.Flex, { alignContent: "center", justifyContent: "center", width: "100%", children: jsxRuntime.jsx(uilibGl.CircularProgress, { diameter: 1.5, mono: true }) }));
12699
12753
 
12700
- // forwardRef чтобы Chart мог измерить фактическую ширину обёртки (fill-режим, useResizeWidth).
12754
+ // forwardRef сохранён для совместимости; в fill-режиме ResizeObserver меряет внешнюю обёртку
12755
+ // ChartFillMeasure (см. Chart/index.tsx), а не сам ChartWrapper.
12701
12756
  const ChartWrapper = React.memo(React.forwardRef(({ width, height, column, loading, children }, ref) => {
12702
12757
  return (jsxRuntime.jsxs(ChartWrapperContainer, { ref: ref, column: column, style: { width, height }, children: [loading && jsxRuntime.jsx(ChartLoading, { column: column }), jsxRuntime.jsx(uilibGl.Flex, { opacity: loading ? FILTERED_VALUE_OPACITY / 100 : 1, children: children })] }));
12703
12758
  }));
@@ -14034,10 +14089,10 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
14034
14089
  const primaryColor = palette.primary;
14035
14090
  const { t } = useGlobalContext();
14036
14091
  const { expandedContainers, attributes, dataSources } = useWidgetContext(type);
14037
- // chartFill контейнера: график тянется на всю ширину. Ширину измеряем реально (ResizeObserver),
14038
- // т.к. d3-графики (и useChartChange для line) требуют пиксельное число, а не CSS "100%".
14039
- const fill = React.useContext(ChartFillContext);
14040
- const [chartRef, measuredWidth] = useResizeWidth(fill);
14092
+ // fill контейнера: вписываем тело графика в контейнер. Размеры ячейки измеряем реально (ResizeObserver),
14093
+ // т.к. d3-графики (и useChartChange для line) требуют пиксельные числа, а не CSS "100%".
14094
+ const { fill, fitHeight } = React.useContext(FillContext);
14095
+ const [chartRef, { width: measuredWidth, height: measuredHeight }] = useResizeBox(fill);
14041
14096
  const { id, options, children } = element || {};
14042
14097
  const { column, markers: configMarkers, showLabels, showMarkers, showTotal, totalWord: configTotalWord, totalAttribute, expandable, expanded, chartType, relatedDataSources, defaultColor, dotSnapping, } = options || {};
14043
14098
  const isLineChart = chartType === "line";
@@ -14073,11 +14128,19 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
14073
14128
  const chartWidth = fill
14074
14129
  ? measuredWidth ?? DEFAULT_CHART_WIDTH
14075
14130
  : toPxNumber(width) ?? DEFAULT_CHART_WIDTH;
14131
+ // Фит по высоте включается только при заданной высоте контейнера (fitHeight) и уже измеренной
14132
+ // ячейке; иначе — прежняя фикс. высота options.height. Флаг важен и ниже: он различает два
14133
+ // смысла chartHeight — «высота тела» (вне fill) и «вся доступная высота» (в fill).
14134
+ const fitsHeight = React.useMemo(() => !!(fill && fitHeight && measuredHeight), [fill, fitHeight, measuredHeight]);
14135
+ // fill по высоте: тянем тело графика на измеренную доступную высоту ячейки (content-box минус слоты).
14136
+ const chartHeight = React.useMemo(() => (fitsHeight && measuredHeight ? measuredHeight : height), [fitsHeight, measuredHeight, height]);
14137
+ // Круглый график вписываем по короткой стороне (object-fit: contain), центрируя в ячейке.
14138
+ const pieSide = React.useMemo(() => (fitsHeight && measuredHeight ? Math.min(chartWidth, measuredHeight) : chartWidth), [fitsHeight, measuredHeight, chartWidth]);
14076
14139
  const [customize] = useChartChange({
14077
14140
  dataSources: config.dataSources,
14078
14141
  chartId: elementConfig?.id,
14079
14142
  width: chartWidth,
14080
- height,
14143
+ height: chartHeight,
14081
14144
  fontColor,
14082
14145
  relatedAttributes,
14083
14146
  defaultColor: primaryColor,
@@ -14177,26 +14240,37 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
14177
14240
  .flat()
14178
14241
  .map(({ values }) => values)
14179
14242
  .flat();
14180
- return (jsxRuntime.jsxs(AnyChartWrapper, { height: height, children: [jsxRuntime.jsx(LineChartStyles, {}), jsxRuntime.jsx(charts.LineChart, { data: lineChartData, labels: labels, markers: markers, width: chartWidth, height: height, xAxisPadding: 15, yAxisPadding: 0, min: Math.min(...chartValues), customYAxisSelection: yAxis => {
14243
+ return (jsxRuntime.jsxs(AnyChartWrapper, { height: chartHeight, children: [jsxRuntime.jsx(LineChartStyles, {}), jsxRuntime.jsx(charts.LineChart, { data: lineChartData, labels: labels, markers: markers, width: chartWidth, height: chartHeight, xAxisPadding: 15, yAxisPadding: 0, min: Math.min(...chartValues), customYAxisSelection: yAxis => {
14181
14244
  if (isHidedY) {
14182
14245
  yAxis.remove();
14183
14246
  }
14184
14247
  }, customYAxis: yAxis => yAxis.ticks(4), renderTooltip: renderLineChartTooltip, customize: customize, dotSnapping: dotSnapping, dynamicTooltipEnable: true, stackedTooltip: true, tooltipClassName: "dashboardLineChartTooltip", drawGridX: !isHidedY, margin: margin })] }));
14185
14248
  }
14186
14249
  if (isStackBar) {
14187
- const stackBarHeight = showTotal ? height + STACK_BAR_TOTAL_HEIGHT : height;
14250
+ // Вне fill chartHeight высота самой полосы, и обёртка честно становится выше на строку
14251
+ // итога. В fill chartHeight — уже ВСЯ доступная высота ячейки, поэтому строку итога надо
14252
+ // вычесть из неё (обёртка остаётся ровно по ячейке), а не прибавить сверху — иначе тело
14253
+ // вылезает за нижнюю границу на STACK_BAR_TOTAL_HEIGHT.
14254
+ const stackBarHeight = showTotal && !fitsHeight ? chartHeight + STACK_BAR_TOTAL_HEIGHT : chartHeight;
14188
14255
  return (jsxRuntime.jsx(AnyChartWrapper, { height: stackBarHeight, children: jsxRuntime.jsx(StackBar, { data: data, filterName: filterName, type: type, alias: elementConfig?.children?.find(child => child.id === "alias"), options: options, renderTooltip: renderPieChartTooltip, renderElement: renderElement }) }));
14189
14256
  }
14190
14257
  if (isPieChart) {
14191
- return (jsxRuntime.jsx(AnyChartWrapper, { height: height, children: jsxRuntime.jsx(charts.PieChart, { data: (data[0]?.items
14258
+ return (jsxRuntime.jsx(AnyChartWrapper, { height: fill ? pieSide : height, children: jsxRuntime.jsx(charts.PieChart, { data: (data[0]?.items
14192
14259
  ?.filter(({ value }) => !isEmptyValue(value))
14193
14260
  ?.map(item => ({
14194
14261
  ...item,
14195
14262
  color: formatFilterColor(item.name, item.color, defaultColor),
14196
- })) || []), width: chartWidth, height: chartWidth, padAngle: angle, outerRadius: radius, cornerRadius: cornerRadius, renderTooltip: renderPieChartTooltip, withTooltip: true, onClick: filterName ? item => onFilter(item.name) : undefined, children: showTotal && (jsxRuntime.jsx(PieChartCenter, { children: totalWord || roundTotalSum(+totalSum) })) }) }));
14263
+ })) || []), width: pieSide, height: pieSide, padAngle: angle, outerRadius: radius, cornerRadius: cornerRadius, renderTooltip: renderPieChartTooltip, withTooltip: true, onClick: filterName ? item => onFilter(item.name) : undefined, children: showTotal && (jsxRuntime.jsx(PieChartCenter, { children: totalWord || roundTotalSum(+totalSum) })) }) }));
14197
14264
  }
14198
14265
  const barChartData = getDataFromFilterItems(data[0]?.items);
14199
- return (jsxRuntime.jsx(BarChartWrapper, { height: height + margin.bottom, children: jsxRuntime.jsx(BarChartContainer, { children: jsxRuntime.jsx(StyledBarChart, { data: barChartData, colors: getColorsFromFilterItems(data[0]?.items, defaultColor, formatFilterColor), minValue: getMinValueFromFilterItems(data[0]?.items), markers: markers, width: chartWidth, height: height, barWidth: barWidth, barPadding: padding, customYAxisLeft: customYAxisLeft, customXAxisBottom: xAxisBottom => customXAxisBottom(xAxisBottom, barChartData), customYAxis: axis => !showLabels && axis.remove(), customBars: ({ bars }) => {
14266
+ // Тот же случай, что у stack: обёртка выше тела на подписи оси X (margin.bottom). В fill
14267
+ // chartHeight — вся доступная высота, поэтому подписи вычитаем из неё, а не прибавляем.
14268
+ // BAR_CHART_FOOTER_MARGIN — это внешний margin-bottom самой BarChartWrapper: в её height он
14269
+ // не входит, но место в ячейке занимает, поэтому его тоже вычитаем.
14270
+ const barBodyHeight = fitsHeight
14271
+ ? chartHeight - margin.bottom - BAR_CHART_FOOTER_MARGIN
14272
+ : chartHeight;
14273
+ return (jsxRuntime.jsx(BarChartWrapper, { height: barBodyHeight + margin.bottom, children: jsxRuntime.jsx(BarChartContainer, { children: jsxRuntime.jsx(StyledBarChart, { data: barChartData, colors: getColorsFromFilterItems(data[0]?.items, defaultColor, formatFilterColor), minValue: getMinValueFromFilterItems(data[0]?.items), markers: markers, width: chartWidth, height: barBodyHeight, barWidth: barWidth, barPadding: padding, customYAxisLeft: customYAxisLeft, customXAxisBottom: xAxisBottom => customXAxisBottom(xAxisBottom, barChartData), customYAxis: axis => !showLabels && axis.remove(), customBars: ({ bars }) => {
14200
14274
  bars.attr("rx", radius);
14201
14275
  bars.attr("ry", radius);
14202
14276
  }, margin: margin, xAxisPadding: 0, yAxisPadding: 0, formatTooltipValue: formatTooltipValue, formatTooltipName: formatTooltipName, hideTooltipGroupName: true, dynamicTooltipEnable: true, isBarTooltip: true, onBarClick: filterName ? item => onFilter(item.name) : undefined }) }) }));
@@ -14214,6 +14288,10 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
14214
14288
  defaultColor,
14215
14289
  formatFilterColor,
14216
14290
  chartWidth,
14291
+ chartHeight,
14292
+ fitsHeight,
14293
+ pieSide,
14294
+ fill,
14217
14295
  barWidth,
14218
14296
  showLabels,
14219
14297
  padding,
@@ -14241,7 +14319,11 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
14241
14319
  ]);
14242
14320
  if (!isVisibleContainer(id, expandable, expanded, expandedContainers))
14243
14321
  return null;
14244
- return (jsxRuntime.jsx(ChartWrapper, { ref: chartRef, width: width, height: isStackBar ? "auto" : isPieChart ? (fill ? chartWidth : width) : height, column: column, loading: loading, children: renderChart }));
14322
+ const chartBody = (jsxRuntime.jsx(ChartWrapper, { width: isPieChart && fill ? pieSide : width, height: isStackBar ? "auto" : isPieChart ? (fill ? pieSide : width) : chartHeight, column: column, loading: loading, children: renderChart }));
14323
+ if (!fill)
14324
+ return chartBody;
14325
+ // fill: тело центрируется в измеряемой обёртке, ref которой питает ResizeObserver обеими осями.
14326
+ return jsxRuntime.jsx(ChartFillMeasure, { ref: chartRef, children: chartBody });
14245
14327
  });
14246
14328
 
14247
14329
  const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, loading }) => {
@@ -15298,7 +15380,7 @@ exports.useRedrawLayer = useRedrawLayer;
15298
15380
  exports.useRelatedDataSourceAttributes = useRelatedDataSourceAttributes;
15299
15381
  exports.useRemoteTask = useRemoteTask;
15300
15382
  exports.useRenderElement = useRenderElement;
15301
- exports.useResizeWidth = useResizeWidth;
15383
+ exports.useResizeBox = useResizeBox;
15302
15384
  exports.useSavePrototypeBuilder = useSavePrototypeBuilder;
15303
15385
  exports.useServerNotificationsContext = useServerNotificationsContext;
15304
15386
  exports.useShownOtherItems = useShownOtherItems;