@evergis/react 4.0.153 → 4.0.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.esm.js CHANGED
@@ -3356,6 +3356,46 @@ const MAX_CHART_WIDTH = 300;
3356
3356
  const DEFAULT_CHART_ANGLE = 4;
3357
3357
  const DEFAULT_CHART_HEIGHT = 90;
3358
3358
  const STACK_BAR_TOTAL_HEIGHT = 20;
3359
+ /** Высота строки текста подписи оси (шрифт 10px) — по макету. */
3360
+ const AXIS_TITLE_LINE_HEIGHT = 12;
3361
+ /** Зазор между подписью оси и значениями шкалы — по макету. */
3362
+ const AXIS_TITLE_GAP = 4;
3363
+ /**
3364
+ * Поле SVG графика под выступ крайних делений шкалы значений.
3365
+ *
3366
+ * d3 центрирует подпись деления на его линии (`dy: 0.32em`), поэтому у крайнего деления половина подписи
3367
+ * выходит за поле графика. Если поле вплотную к краю SVG, половина оказывается за SVG, и её срезает
3368
+ * обёртка: у линейного графика в fill — `ChartFillMeasure` (`overflow: hidden`), у барчарта всегда —
3369
+ * `BarChartContainer` (`overflow-x: hidden`, отчего и по вертикали содержимое не `visible`).
3370
+ */
3371
+ const Y_AXIS_LABEL_OVERHANG = 6;
3372
+ /** Зазор от строки горизонтальных подписей осей до верхней линии сетки — по макету. */
3373
+ const AXIS_TITLE_TOP_GRID_GAP = 9;
3374
+ /** Число делений шкал линейного графика; у левой и правой шкал оно общее, чтобы совпадала сетка. */
3375
+ const LINE_CHART_Y_TICKS = 4;
3376
+ /**
3377
+ * Высота строки горизонтальных подписей осей (`axis.titlePosition: "top"`). Вместе с верхним полем SVG
3378
+ * (Y_AXIS_LABEL_OVERHANG) даёт строку текста и 9px до верхней линии сетки: деление центрировано на
3379
+ * линии и выступает над ней на ~5px, так что между подписью и значением шкалы остаются те же 4px,
3380
+ * что и у вертикальной подписи.
3381
+ */
3382
+ const AXIS_TITLE_TOP_HEIGHT = AXIS_TITLE_LINE_HEIGHT + AXIS_TITLE_TOP_GRID_GAP - Y_AXIS_LABEL_OVERHANG;
3383
+ /**
3384
+ * Пустая полоса под подписями оси X внутри SVG линейного графика: под ось зарезервировано
3385
+ * LINE_CHART_LABEL_MARGIN (20px), а подписи сдвинуты вниз на +16px, поэтому строка подписи
3386
+ * (шрифт d3 10px) кончается примерно в 14px от низа SVG.
3387
+ */
3388
+ const LINE_CHART_X_LABELS_BLANK = 14;
3389
+ /** Зазор между подписями оси X и подписью самой оси — по макету. */
3390
+ const X_AXIS_TITLE_GAP = 4;
3391
+ /** Зазор между подписью оси X и легендой, идущей отдельной строкой, — по макету. */
3392
+ const X_AXIS_TITLE_LEGEND_GAP = 8;
3393
+ /** Выравнивание подписи оси X → `justify-content` строки-подвала графика. */
3394
+ const CHART_AXIS_ALIGN_JUSTIFY = {
3395
+ left: "flex-start",
3396
+ center: "center",
3397
+ right: "flex-end",
3398
+ };
3359
3399
  const FILTER_PREFIX = "%";
3360
3400
  /** Имя системного геометрического фильтра — выделение, нарисованное пользователем на карте. */
3361
3401
  const GEOMETRY_FILTER_NAME = "geometry";
@@ -3465,7 +3505,76 @@ const CONTAINER_BODY_FILL_STYLE = {
3465
3505
  */
3466
3506
  const CONTAINERS_GROUP_DEFAULTS = { boxSizing: "border-box" };
3467
3507
 
3468
- const ChartTooltipTable = styled.table.withConfig({ displayName: "ChartTooltipTable", componentId: "sc-1fk3ts5" }) `
3508
+ /** Обёртка линейного графика: строка горизонтальных подписей над телом, вертикальные — по бокам. */
3509
+ const LineChartLayout = styled.div.withConfig({ displayName: "LineChartLayout", componentId: "sc-gmzloe" }) `
3510
+ display: flex;
3511
+ flex-direction: column;
3512
+ width: 100%;
3513
+ height: 100%;
3514
+ min-width: 0;
3515
+ min-height: 0;
3516
+ `;
3517
+ const LineChartRow = styled.div.withConfig({ displayName: "LineChartRow", componentId: "sc-dxzi1m" }) `
3518
+ display: flex;
3519
+ flex: 1 1 auto;
3520
+ align-items: stretch;
3521
+ min-width: 0;
3522
+ min-height: 0;
3523
+ `;
3524
+ /**
3525
+ * Тело линейного графика между вертикальными подписями осей. Забирает всю оставшуюся ширину строки —
3526
+ * её и измеряем, чтобы отдать d3 пиксельную ширину: подписи бывают в одну и в две строки.
3527
+ */
3528
+ const LineChartBody = styled.div.withConfig({ displayName: "LineChartBody", componentId: "sc-1sbsc7u" }) `
3529
+ flex: 1 1 auto;
3530
+ min-width: 0;
3531
+ `;
3532
+ const AxisTitlesRow = styled.div.withConfig({ displayName: "AxisTitlesRow", componentId: "sc-1hskx6f" }) `
3533
+ display: flex;
3534
+ flex: 0 0 ${AXIS_TITLE_TOP_HEIGHT}px;
3535
+ justify-content: space-between;
3536
+ align-items: flex-start;
3537
+ gap: 0.5rem;
3538
+ height: ${AXIS_TITLE_TOP_HEIGHT}px;
3539
+ min-width: 0;
3540
+ `;
3541
+ const AxisTitleText = styled.div.withConfig({ displayName: "AxisTitleText", componentId: "sc-9shs4l" }) `
3542
+ font-size: 0.625rem;
3543
+ line-height: ${AXIS_TITLE_LINE_HEIGHT}px;
3544
+ color: ${({ $color, theme: { palette } }) => $color || palette.iconDisabled};
3545
+ `;
3546
+ const AxisTitleTop = styled(AxisTitleText).withConfig({ displayName: "AxisTitleTop", componentId: "sc-pv3dgm" }) `
3547
+ overflow: hidden;
3548
+ white-space: nowrap;
3549
+ text-overflow: ellipsis;
3550
+ `;
3551
+ /**
3552
+ * Колонка вертикальной подписи оси: ширина — по тексту (одна или две строки) плюс зазор до значений
3553
+ * шкалы. Вертикальные отступы колонки повторяют поля SVG, поэтому подпись центрируется по полю
3554
+ * графика, а не по всей высоте вместе с подписями оси X.
3555
+ */
3556
+ const AxisTitleSideColumn = styled.div.withConfig({ displayName: "AxisTitleSideColumn", componentId: "sc-11nmkfu" }) `
3557
+ display: flex;
3558
+ flex: 0 0 auto;
3559
+ align-items: center;
3560
+ justify-content: center;
3561
+ box-sizing: border-box;
3562
+ padding: ${({ $plotTop }) => $plotTop}px 0 ${({ $plotBottom }) => $plotBottom}px;
3563
+ ${({ $side }) => ($side === "left" ? "padding-right" : "padding-left")}: ${AXIS_TITLE_GAP}px;
3564
+ `;
3565
+ /**
3566
+ * Сам текст вертикальной подписи. Поворот вынесен на внутренний элемент: повёрнутая целиком колонка
3567
+ * перенесла бы зазор до шкалы на внешнюю сторону. Левая подпись читается снизу вверх, правая — сверху
3568
+ * вниз, как на макете.
3569
+ */
3570
+ const AxisTitleSide = styled(AxisTitleText).withConfig({ displayName: "AxisTitleSide", componentId: "sc-lretfl" }) `
3571
+ max-height: 100%;
3572
+ overflow: hidden;
3573
+ text-align: center;
3574
+ writing-mode: vertical-rl;
3575
+ transform: ${({ $side }) => ($side === "left" ? "rotate(180deg)" : "none")};
3576
+ `;
3577
+ const ChartTooltipTable = styled.table.withConfig({ displayName: "ChartTooltipTable", componentId: "sc-1j0a1z" }) `
3469
3578
  td {
3470
3579
  padding: 0;
3471
3580
  }
@@ -3474,7 +3583,7 @@ const ChartTooltipTable = styled.table.withConfig({ displayName: "ChartTooltipTa
3474
3583
  padding-bottom: 0.375rem;
3475
3584
  }
3476
3585
  `;
3477
- const ChartTooltip = styled(Flex).withConfig({ displayName: "ChartTooltip", componentId: "sc-1iyyyv7" }) `
3586
+ const ChartTooltip = styled(Flex).withConfig({ displayName: "ChartTooltip", componentId: "sc-1ps09qa" }) `
3478
3587
  flex-wrap: nowrap;
3479
3588
  background: rgb(48, 69, 79);
3480
3589
  border-radius: 0.25rem;
@@ -3484,26 +3593,26 @@ const ChartTooltip = styled(Flex).withConfig({ displayName: "ChartTooltip", comp
3484
3593
  margin-bottom: 0.25rem;
3485
3594
  }
3486
3595
  `;
3487
- const ChartTooltipLabel = styled.div.withConfig({ displayName: "ChartTooltipLabel", componentId: "sc-gl0qxz" }) `
3596
+ const ChartTooltipLabel = styled.div.withConfig({ displayName: "ChartTooltipLabel", componentId: "sc-1chgokj" }) `
3488
3597
  margin-right: 0.25rem;
3489
3598
  `;
3490
- const ChartTooltipColor = styled.div.withConfig({ displayName: "ChartTooltipColor", componentId: "sc-tmvvbh" }) `
3599
+ const ChartTooltipColor = styled.div.withConfig({ displayName: "ChartTooltipColor", componentId: "sc-1un94mw" }) `
3491
3600
  width: 0.625rem;
3492
3601
  height: 0.625rem;
3493
3602
  background-color: ${({ $color }) => $color};
3494
3603
  border-radius: ${({ theme: { borderRadius } }) => borderRadius.smallest};
3495
3604
  margin-right: 0.25rem;
3496
3605
  `;
3497
- const ChartLegendColor$1 = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-l66k4q" }) `
3606
+ const ChartLegendColor$1 = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-sw1cfw" }) `
3498
3607
  width: 0.5rem;
3499
3608
  height: 0.5rem;
3500
3609
  background-color: ${({ $color }) => $color};
3501
3610
  border-radius: 0.125rem;
3502
3611
  `;
3503
- const ChartLegendValue = styled.div.withConfig({ displayName: "ChartLegendValue", componentId: "sc-1d7z775" }) `
3612
+ const ChartLegendValue = styled.div.withConfig({ displayName: "ChartLegendValue", componentId: "sc-1s1kjum" }) `
3504
3613
  opacity: 0.65;
3505
3614
  `;
3506
- const ChartTooltipRow = styled(Flex).withConfig({ displayName: "ChartTooltipRow", componentId: "sc-ih1uc5" }) `
3615
+ const ChartTooltipRow = styled(Flex).withConfig({ displayName: "ChartTooltipRow", componentId: "sc-ftllia" }) `
3507
3616
  display: flex;
3508
3617
  flex-direction: row;
3509
3618
  flex-wrap: nowrap;
@@ -3515,11 +3624,11 @@ const ChartTooltipRow = styled(Flex).withConfig({ displayName: "ChartTooltipRow"
3515
3624
  margin-right: 0.25rem;
3516
3625
  }
3517
3626
  `;
3518
- const ChartTooltipName = styled.div.withConfig({ displayName: "ChartTooltipName", componentId: "sc-sm4g0t" }) `
3627
+ const ChartTooltipName = styled.div.withConfig({ displayName: "ChartTooltipName", componentId: "sc-16ov53z" }) `
3519
3628
  margin-right: 1rem;
3520
3629
  opacity: 0.65;
3521
3630
  `;
3522
- const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", componentId: "sc-1le93jy" }) `
3631
+ const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", componentId: "sc-5m536c" }) `
3523
3632
  position: absolute;
3524
3633
  top: 50%;
3525
3634
  left: 50%;
@@ -3528,7 +3637,7 @@ const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", co
3528
3637
  font-weight: bold;
3529
3638
  font-size: 1.5rem;
3530
3639
  `;
3531
- const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapperContainer", componentId: "sc-1f4x9zv" }) `
3640
+ const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapperContainer", componentId: "sc-1ixngl" }) `
3532
3641
  position: relative;
3533
3642
  width: 100%;
3534
3643
  `;
@@ -3537,7 +3646,7 @@ const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapper
3537
3646
  * на неё вешается ref для ResizeObserver — меряем обе оси без цикла. Плоский div (не Flex uilib), как соседний
3538
3647
  * ChartWrapperContainer, чтобы ref гарантированно дошёл до DOM-узла. Тело графика центрируется внутри.
3539
3648
  */
3540
- const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure", componentId: "sc-1r4oerb" }) `
3649
+ const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure", componentId: "sc-3rqqmt" }) `
3541
3650
  position: relative;
3542
3651
  display: flex;
3543
3652
  align-items: center;
@@ -3555,7 +3664,7 @@ const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure"
3555
3664
  display: block;
3556
3665
  }
3557
3666
  `;
3558
- const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-1va8cy3" }) `
3667
+ const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-k91v0" }) `
3559
3668
  position: relative;
3560
3669
  border-radius: 0.25rem;
3561
3670
  background-color: rgba(28, 33, 48);
@@ -3660,7 +3769,7 @@ const LineChartStyles = createGlobalStyle `
3660
3769
  fill: ${({ theme: { palette } }) => palette.textDisabled};
3661
3770
  }
3662
3771
  `;
3663
- const StyledBarChart = styled(BarChart$1).withConfig({ displayName: "StyledBarChart", componentId: "sc-1l1kx80" }) `
3772
+ const StyledBarChart = styled(BarChart$1).withConfig({ displayName: "StyledBarChart", componentId: "sc-16gz38d" }) `
3664
3773
  .domain {
3665
3774
  display: none;
3666
3775
  }
@@ -4548,6 +4657,79 @@ const getAttributeConfigurationByName = (attributes, name) => {
4548
4657
  return attributes[name];
4549
4658
  };
4550
4659
 
4660
+ const EMPTY_AXIS = {};
4661
+ const warnedLegacySources = new Set();
4662
+ // Сборщик консьюмера подставляет `process.env.NODE_ENV` строкой, поэтому выражение должно быть
4663
+ // ровно таким — без `typeof process` и без `?.`. Сам `process` webpack 5 в браузер не завозит:
4664
+ // проверка `typeof process` отключила бы предупреждения и в dev. Консьюмер без сборщика получит
4665
+ // ReferenceError — он гасится, и предупреждений просто нет.
4666
+ const isDevEnvironment = (() => {
4667
+ try {
4668
+ return process.env.NODE_ENV !== "production";
4669
+ }
4670
+ catch {
4671
+ return false;
4672
+ }
4673
+ })();
4674
+ /**
4675
+ * Один раз на источник сообщает, что конфиг остался на устаревшей плоской форме.
4676
+ *
4677
+ * Без такого маркера момент «легаси закончилось» не наступает никогда, и временный слой
4678
+ * совместимости становится постоянным.
4679
+ */
4680
+ const warnLegacyAxis = ({ dataSourceName, attributeName }) => {
4681
+ if (!isDevEnvironment)
4682
+ return;
4683
+ const key = `${dataSourceName ?? "?"}:${attributeName ?? "?"}`;
4684
+ if (warnedLegacySources.has(key))
4685
+ return;
4686
+ warnedLegacySources.add(key);
4687
+ // eslint-disable-next-line no-console
4688
+ console.warn(`[Dashboard] Источник "${key}" использует устаревшие поля оси (chartAxis/axisColor/hideAxis). ` +
4689
+ "Перепишите их в объект axis: { type, side, color, title, hide }. " +
4690
+ "Слой совместимости временный.");
4691
+ };
4692
+ /**
4693
+ * Настройки оси источника.
4694
+ *
4695
+ * Читает `axis`, а при его отсутствии собирает объект из устаревших плоских полей
4696
+ * `chartAxis` / `axisColor` / `hideAxis`. Заданный `axis` имеет приоритет: узел, который уже
4697
+ * правили в редакторе, не должен откатываться к старым значениям.
4698
+ */
4699
+ const resolveAxis = (source) => {
4700
+ if (!source)
4701
+ return EMPTY_AXIS;
4702
+ const { axis, chartAxis, axisColor, hideAxis } = source;
4703
+ if (axis)
4704
+ return axis;
4705
+ if (chartAxis === undefined && axisColor === undefined && hideAxis === undefined)
4706
+ return EMPTY_AXIS;
4707
+ warnLegacyAxis(source);
4708
+ return {
4709
+ type: chartAxis === "x" ? "x" : "y",
4710
+ color: axisColor,
4711
+ hide: hideAxis,
4712
+ };
4713
+ };
4714
+ /**
4715
+ * Источник питает ось значений.
4716
+ *
4717
+ * Отсутствие `axis.type` означает `"y"`: конфиг без явного типа — самый частый случай,
4718
+ * и раньше такой источник молча выпадал из графика.
4719
+ */
4720
+ const isValueAxis = (source) => (resolveAxis(source).type ?? "y") === "y";
4721
+ /** Сторона оси; отсутствие `axis.side` означает `"left"`. */
4722
+ const getAxisSide = (source) => resolveAxis(source).side ?? "left";
4723
+ /** Источник выводится на правой оси. */
4724
+ const isRightAxis = (source) => getAxisSide(source) === "right";
4725
+ /**
4726
+ * Запись описывает ось чарта, а не источник фильтра.
4727
+ *
4728
+ * `BarChartFilter` собирает `relatedConfig` вручную и осей не задаёт — ему нужна исходная палитра,
4729
+ * а не растянутая под число элементов.
4730
+ */
4731
+ const hasAxisConfig = ({ axis, chartAxis }) => axis !== undefined || chartAxis !== undefined;
4732
+
4551
4733
  const getAttributesConfiguration = (layer) => {
4552
4734
  const layerAttributeConfiguration = layer?.configuration?.attributesConfiguration ?? {};
4553
4735
  const { geometryAttribute, idAttribute } = layerAttributeConfiguration;
@@ -4572,7 +4754,7 @@ const getAttributesConfiguration = (layer) => {
4572
4754
 
4573
4755
  const formatChartRelatedValue = (t, value, layerInfo, relatedAttributes) => {
4574
4756
  const layerDefinition = getAttributesConfiguration(layerInfo);
4575
- const relatedAxis = relatedAttributes.find(({ chartAxis }) => chartAxis === "y");
4757
+ const relatedAxis = relatedAttributes.find(isValueAxis);
4576
4758
  const attribute = getAttributeConfigurationByName(layerDefinition.attributes, relatedAxis?.attributeName);
4577
4759
  return attribute
4578
4760
  ? formatAttributeValue({ t, type: attribute.type, value, stringFormat: attribute.stringFormat })
@@ -5417,11 +5599,11 @@ const getAttributeValue = (element, attributes) => {
5417
5599
  return typeof value === "string" && maxLength && maxLength < value.length ? (jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, expandable: expandable, lineBreak: lineBreak, children: value })) : (value);
5418
5600
  };
5419
5601
 
5420
- const getChartAxes = (chartElement) => chartElement?.options?.relatedDataSources?.filter(({ chartAxis }) => chartAxis === "y");
5602
+ const getChartAxes = (chartElement) => chartElement?.options?.relatedDataSources?.filter(isValueAxis);
5421
5603
 
5422
5604
  const getChartFilterName = (relatedDataSources) => {
5423
5605
  const relatedAttributes = relatedDataSources || [];
5424
- const axes = relatedAttributes.filter(({ chartAxis }) => chartAxis === "y");
5606
+ const axes = relatedAttributes.filter(isValueAxis);
5425
5607
  return axes?.[0]?.filterName;
5426
5608
  };
5427
5609
 
@@ -7051,6 +7233,96 @@ const useFetchImageWithAuth = (url) => {
7051
7233
  return isExternal ? url : blobUrl;
7052
7234
  };
7053
7235
 
7236
+ /**
7237
+ * Оси значений линейного графика и атрибуты, по `stringFormat` которых форматируются деления.
7238
+ *
7239
+ * У левой и правой осей это разные атрибуты, поэтому они резолвятся по отдельности: иначе на
7240
+ * правой оси окажутся единицы измерения левой.
7241
+ */
7242
+ const useChartAxisAttributes = ({ dataSources, relatedAttributes }) => {
7243
+ const { layerInfos, dataSources: fetchedDataSources } = useWidgetContext();
7244
+ const axes = useMemo(() => (relatedAttributes || []).filter(isValueAxis), [relatedAttributes]);
7245
+ const strokeColors = useMemo(() => axes.map(related => resolveAxis(related).color), [axes]);
7246
+ const leftAxis = useMemo(() => axes.find(related => !isRightAxis(related)) ?? axes[0] ?? relatedAttributes?.[0], [axes, relatedAttributes]);
7247
+ const rightAxis = useMemo(() => axes.find(isRightAxis), [axes]);
7248
+ const resolveAxisAttribute = useCallback((related) => {
7249
+ if (!related)
7250
+ return undefined;
7251
+ const layerInfo = getDataSourceLayerInfo({
7252
+ layerInfos,
7253
+ configDataSource: dataSources?.find(({ name }) => name === related.dataSourceName),
7254
+ fetchedDataSource: getDataSource(related.dataSourceName, fetchedDataSources),
7255
+ });
7256
+ return getLayerInfoAttribute(layerInfo, related.attributeName);
7257
+ }, [dataSources, fetchedDataSources, layerInfos]);
7258
+ const leftAxisAttribute = useMemo(() => resolveAxisAttribute(leftAxis), [resolveAxisAttribute, leftAxis]);
7259
+ const rightAxisAttribute = useMemo(() => resolveAxisAttribute(rightAxis), [resolveAxisAttribute, rightAxis]);
7260
+ return { axes, strokeColors, leftAxisAttribute, rightAxisAttribute };
7261
+ };
7262
+
7263
+ /**
7264
+ * Форматтеры делений левой и правой шкал линейного графика — по `stringFormat` своего атрибута.
7265
+ *
7266
+ * Отдаются в `tickFormat` генератора оси, а не подменяют текст после отрисовки: d3 измеряет ширину
7267
+ * шкалы по уже нарисованным подписям и по ней сдвигает поле графика. Подмена текста задним числом
7268
+ * (`30` → `30,0`) делала подпись шире замера, и она заезжала на подпись оси слева.
7269
+ */
7270
+ const useChartAxisTickFormat = ({ dataSources, relatedAttributes }) => {
7271
+ const { t } = useGlobalContext();
7272
+ const { leftAxisAttribute, rightAxisAttribute } = useChartAxisAttributes({ dataSources, relatedAttributes });
7273
+ const createTickFormat = useCallback((attribute) => (value) => {
7274
+ if (!value)
7275
+ return "0";
7276
+ return attribute
7277
+ ? String(formatAttributeValue({
7278
+ t,
7279
+ type: attribute.type,
7280
+ value: Number(value),
7281
+ stringFormat: attribute.stringFormat,
7282
+ noUnits: true,
7283
+ }))
7284
+ : String(value);
7285
+ }, [t]);
7286
+ const formatLeftTick = useMemo(() => createTickFormat(leftAxisAttribute), [createTickFormat, leftAxisAttribute]);
7287
+ const formatRightTick = useMemo(() => createTickFormat(rightAxisAttribute), [createTickFormat, rightAxisAttribute]);
7288
+ return { formatLeftTick, formatRightTick };
7289
+ };
7290
+
7291
+ /**
7292
+ * Подписи осей линейного графика и место, которое они занимают над телом графика.
7293
+ *
7294
+ * Режим размещения общий для всех осей (`options.axis.titlePosition`), текст — у каждой оси свой.
7295
+ * Цвет подписи отдельной опцией не задаётся: одна серия на стороне — подпись в её цвет, несколько —
7296
+ * нейтральная, потому что тогда подпись описывает всю ось, а не одну из линий. Ширину вертикальных
7297
+ * подписей здесь не считаем — она зависит от числа строк текста и измеряется по факту.
7298
+ */
7299
+ const useChartAxisTitles = ({ options, axes, isLineChart }) => {
7300
+ const axisTitlePosition = options?.axis?.titlePosition ?? "side";
7301
+ const axisTitles = useMemo(() => {
7302
+ const pick = (side) => {
7303
+ const sideAxes = axes.filter(related => getAxisSide(related) === side);
7304
+ const title = sideAxes.map(resolveAxis).find(axis => axis.title)?.title;
7305
+ if (!title)
7306
+ return undefined;
7307
+ return { title, color: sideAxes.length === 1 ? resolveAxis(sideAxes[0]).color : undefined };
7308
+ };
7309
+ return { left: pick("left"), right: pick("right") };
7310
+ }, [axes]);
7311
+ const hasAxisTitles = !!(isLineChart && (axisTitles.left || axisTitles.right));
7312
+ const hasTopTitles = hasAxisTitles && axisTitlePosition === "top";
7313
+ const hasSideTitles = hasAxisTitles && axisTitlePosition === "side";
7314
+ const hideLeftAxis = useMemo(() => axes.filter(related => !isRightAxis(related)).every(related => resolveAxis(related).hide), [axes]);
7315
+ const hideRightAxis = useMemo(() => axes.filter(isRightAxis).every(related => resolveAxis(related).hide), [axes]);
7316
+ return {
7317
+ axisTitles,
7318
+ hasTopTitles,
7319
+ hasSideTitles,
7320
+ axisTitlesHeight: hasTopTitles ? AXIS_TITLE_TOP_HEIGHT : 0,
7321
+ hideLeftAxis,
7322
+ hideRightAxis,
7323
+ };
7324
+ };
7325
+
7054
7326
  const HEIGHT_OFFSET = 20;
7055
7327
  const FILL_OPACITY = 0.28;
7056
7328
  const lineGenerator = line()
@@ -7096,20 +7368,10 @@ function wrap() {
7096
7368
  }
7097
7369
  }
7098
7370
  const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes, defaultColor, fontColor, markers, showMarkers, }) => {
7099
- const { t } = useGlobalContext();
7100
- const { layerInfos, dataSources: fetchedDataSources } = useWidgetContext();
7101
- const strokeColors = relatedAttributes
7102
- .filter(({ chartAxis }) => chartAxis === "y")
7103
- .map(({ axisColor }) => axisColor);
7104
- const yAxis = useMemo(() => relatedAttributes?.find(({ chartAxis }) => chartAxis === "y") ?? relatedAttributes?.[0], [relatedAttributes]);
7105
- const yAxisAttribute = useMemo(() => {
7106
- const layerInfo = getDataSourceLayerInfo({
7107
- layerInfos,
7108
- configDataSource: dataSources?.find(({ name }) => name === yAxis?.dataSourceName),
7109
- fetchedDataSource: getDataSource(yAxis?.dataSourceName, fetchedDataSources),
7110
- });
7111
- return getLayerInfoAttribute(layerInfo, yAxis?.attributeName);
7112
- }, [dataSources, fetchedDataSources, layerInfos, yAxis]);
7371
+ const { strokeColors } = useChartAxisAttributes({
7372
+ dataSources,
7373
+ relatedAttributes,
7374
+ });
7113
7375
  const ref = useRef({
7114
7376
  path: null,
7115
7377
  area: null,
@@ -7162,22 +7424,9 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
7162
7424
  svg
7163
7425
  .selectAll(`.${lineChartClassNames.lineChartXScaleGlobal} .tick text`)
7164
7426
  .attr("y", 16);
7165
- svg
7166
- .selectAll(`.${lineChartClassNames.lineChartYScaleGlobal} .tick text`)
7167
- .text(item => {
7168
- if (!item)
7169
- return 0;
7170
- return yAxisAttribute
7171
- ? formatAttributeValue({
7172
- t,
7173
- type: yAxisAttribute.type,
7174
- value: item,
7175
- stringFormat: yAxisAttribute.stringFormat,
7176
- noUnits: true,
7177
- })
7178
- : item;
7179
- })
7180
- .each(wrap);
7427
+ // Текст делений форматируется ещё в генераторе оси (useChartAxisTickFormat), до замера ширины
7428
+ // шкалы; здесь его только укорачиваем.
7429
+ svg.selectAll(`.${lineChartClassNames.lineChartYScaleGlobal} .tick text`).each(wrap);
7181
7430
  svg.selectAll(".tick text").attr("style", `color: ${fontColor}`);
7182
7431
  const global = svg.select(`.${lineChartClassNames.lineChartLinesGlobal}`);
7183
7432
  const lineChartLines = svg.selectAll(`.${lineChartClassNames.lineChartLine}`);
@@ -7213,7 +7462,6 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
7213
7462
  svg.append("defs").html(() => defs.join(""));
7214
7463
  }, [
7215
7464
  fontColor,
7216
- yAxisAttribute,
7217
7465
  chartId,
7218
7466
  strokeColors,
7219
7467
  defaultColor,
@@ -7221,7 +7469,6 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
7221
7469
  onChange,
7222
7470
  showMarkers,
7223
7471
  markers,
7224
- t,
7225
7472
  ]);
7226
7473
  return [customize, onChange];
7227
7474
  };
@@ -7378,10 +7625,10 @@ const useChartData = ({ element, type }) => {
7378
7625
  const configDataSources = useConfigDataSources(type);
7379
7626
  const relatedAttributes = useMemo(() => element?.options?.relatedDataSources || [], [element?.options?.relatedDataSources]);
7380
7627
  const loading = useMemo(() => !!relatedAttributes?.length &&
7381
- !dataSources?.some(({ name }) => relatedAttributes.some(({ chartAxis, dataSourceName }) => chartAxis === "y" && dataSourceName === name)), [dataSources, relatedAttributes]);
7628
+ !dataSources?.some(({ name }) => relatedAttributes.some(related => isValueAxis(related) && related.dataSourceName === name)), [dataSources, relatedAttributes]);
7382
7629
  const fetchedData = useMemo(() => {
7383
7630
  const isRelated = !!relatedAttributes?.length;
7384
- const filteredAttributes = relatedAttributes.filter(({ chartAxis }) => chartAxis === "y");
7631
+ const filteredAttributes = relatedAttributes.filter(isValueAxis);
7385
7632
  if (!element)
7386
7633
  return [];
7387
7634
  if (isRelated) {
@@ -7389,7 +7636,7 @@ const useChartData = ({ element, type }) => {
7389
7636
  return [];
7390
7637
  return filteredAttributes
7391
7638
  .map(relatedAxis => {
7392
- const { dataSourceName, axisColor, attributeName, attributeUnits } = relatedAxis;
7639
+ const { dataSourceName, attributeName, attributeUnits } = relatedAxis;
7393
7640
  const dataSource = configDataSources.find(({ name }) => name === dataSourceName);
7394
7641
  const featureDataSource = getDataSource(dataSourceName, dataSources);
7395
7642
  const layerInfo = getDataSourceLayerInfo({
@@ -7409,7 +7656,10 @@ const useChartData = ({ element, type }) => {
7409
7656
  layerInfo,
7410
7657
  })
7411
7658
  : null,
7412
- color: axisColor,
7659
+ color: resolveAxis(relatedAxis).color,
7660
+ // Сторона едет вместе с данными: серии без значений отсеиваются ниже, и сопоставлять
7661
+ // их с осями по индексу уже нельзя.
7662
+ axisSide: getAxisSide(relatedAxis),
7413
7663
  attributeName,
7414
7664
  attributeUnits,
7415
7665
  dataSourceName,
@@ -7422,6 +7672,7 @@ const useChartData = ({ element, type }) => {
7422
7672
  return [
7423
7673
  {
7424
7674
  items: getDataFromAttributes(t, element, attributes),
7675
+ axisSide: "left",
7425
7676
  layerInfo: null,
7426
7677
  attributeName: null,
7427
7678
  attributeUnits: null,
@@ -7441,7 +7692,7 @@ const useChartData = ({ element, type }) => {
7441
7692
  ]);
7442
7693
  const isResolved = useMemo(() => !relatedAttributes?.length ||
7443
7694
  relatedAttributes
7444
- .filter(({ chartAxis }) => chartAxis === "y")
7695
+ .filter(isValueAxis)
7445
7696
  .every(({ dataSourceName }) => Array.isArray(getDataSource(dataSourceName, dataSources)?.features)), [relatedAttributes, dataSources]);
7446
7697
  const [data, setData] = useState(fetchedData);
7447
7698
  useEffect(() => {
@@ -8973,6 +9224,15 @@ const ChartLegendContainer = styled(Flex).withConfig({ displayName: "ChartLegend
8973
9224
  flex-direction: column;
8974
9225
  flex-wrap: wrap;
8975
9226
  justify-content: ${({ twoColumns }) => (twoColumns ? "flex-start" : "center")};
9227
+
9228
+ /* Явная опция column перебивает раскладку, которую легенде навязывает ChartContainer
9229
+ (под графиком — в ряд, сбоку — столбиком). Без опции поведение прежнее. */
9230
+ ${({ $column }) => $column !== undefined &&
9231
+ css `
9232
+ &&& {
9233
+ flex-direction: ${$column ? "column" : "row"};
9234
+ }
9235
+ `}
8976
9236
  `;
8977
9237
  const ChartLegendItem = styled(Flex).withConfig({ displayName: "ChartLegendItem", componentId: "sc-16m9piw" }) `
8978
9238
  cursor: ${({ onClick }) => (onClick ? "pointer" : "default")};
@@ -9041,12 +9301,129 @@ const ContainerChart = styled(Flex).withConfig({ displayName: "ContainerChart",
9041
9301
  `}
9042
9302
  `;
9043
9303
  const ContainerLegend = styled(Flex).withConfig({ displayName: "ContainerLegend", componentId: "sc-z1n1s8" }) ``;
9044
- const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-1vurgy9" }) `
9304
+ /**
9305
+ * Выравнивание строки по полю графика: ширина тела графика, а внутренние отступы — до краёв поля
9306
+ * (колонка подписи оси и значения шкалы). Тело графика центрируется в слоте, поэтому и строка
9307
+ * центрируется. Правило поднято `&&&` над шириной, которую строкам задаёт `ContainerValue`.
9308
+ */
9309
+ const plotInsetsMixin = css `
9310
+ ${({ $insets }) => $insets &&
9311
+ css `
9312
+ &&& {
9313
+ box-sizing: border-box;
9314
+ align-self: center;
9315
+ width: ${$insets.width}px;
9316
+ max-width: 100%;
9317
+ padding-left: ${$insets.left}px;
9318
+ padding-right: ${$insets.right}px;
9319
+ }
9320
+ `}
9321
+ `;
9322
+ /**
9323
+ * Строка ждёт, пока график сообщит положение поля: место занимает, но не видна. Иначе до первой
9324
+ * отрисовки d3 она стояла бы по краю контейнера и потом прыгала на место.
9325
+ */
9326
+ const awaitingPlotMixin = css `
9327
+ ${({ $awaitingPlot }) => $awaitingPlot &&
9328
+ css `
9329
+ visibility: hidden;
9330
+ `}
9331
+ `;
9332
+ /**
9333
+ * Строка под телом графика: подпись оси X и, при `legendInline`, легенда справа от неё.
9334
+ *
9335
+ * `$offsetTop` подтягивает строку к подписям оси X: под ними внутри SVG остаётся пустая полоса,
9336
+ * и без поправки зазор получился бы втрое больше макетного.
9337
+ */
9338
+ const ChartFooter = styled(Flex).withConfig({ displayName: "ChartFooter", componentId: "sc-nii28w" }) `
9339
+ align-items: center;
9340
+ flex-wrap: nowrap;
9341
+ gap: 0.5rem;
9342
+ width: 100%;
9343
+ margin-top: ${({ $offsetTop }) => $offsetTop ?? 0}px;
9344
+ justify-content: ${({ $align }) => $align};
9345
+
9346
+ ${ContainerLegend} {
9347
+ justify-content: flex-end;
9348
+ margin-left: auto;
9349
+ }
9350
+
9351
+ /* Легенда в строке с подписью: записи прижаты вправо, без отступа сверху, который ей задаёт
9352
+ ContainerValue, и без нижнего отступа записей — иначе она съедет вниз относительно подписи.
9353
+ Прижимать нужно сами записи: обёртки легенды — Flex из uilib-gl с width: 100%, и выравнивание
9354
+ внешнего контейнера до записей не доходит, а своё у легенды — по центру. */
9355
+ &&& ${ChartLegendContainer} {
9356
+ justify-content: flex-end;
9357
+ margin-top: 0;
9358
+ }
9359
+
9360
+ &&& ${ChartLegendItem} {
9361
+ margin-bottom: 0;
9362
+ }
9363
+
9364
+ ${plotInsetsMixin};
9365
+ ${awaitingPlotMixin};
9366
+ `;
9367
+ const ChartXAxisTitle = styled.div.withConfig({ displayName: "ChartXAxisTitle", componentId: "sc-1vsgzi9" }) `
9368
+ font-size: 0.625rem;
9369
+ line-height: 0.75rem;
9370
+ white-space: nowrap;
9371
+ color: ${({ $color, theme: { palette } }) => $color || palette.textDisabled};
9372
+ `;
9373
+ /**
9374
+ * Строка легенды под графиком. `$gapTop` заменяет отступ сверху, который легенде задаёт `ContainerValue`;
9375
+ * `$offsetTop` сдвигает саму строку — подтягивает её к подписям оси X, как строку с подписью оси.
9376
+ *
9377
+ * `$justify` выравнивает сами записи. Выравнивание строки (`justifyContent`) до них не доходит: обёртки
9378
+ * легенды — Flex из uilib-gl с `width: 100%`, а записи внутри легенда всегда центрирует.
9379
+ */
9380
+ const ChartLegendRow = styled(ContainerLegend).withConfig({ displayName: "ChartLegendRow", componentId: "sc-1a12wrz" }) `
9381
+ ${({ $offsetTop }) => $offsetTop !== undefined &&
9382
+ css `
9383
+ margin-top: ${$offsetTop}px;
9384
+ `}
9385
+
9386
+ ${({ $gapTop }) => $gapTop !== undefined &&
9387
+ css `
9388
+ &&& ${ChartLegendContainer} {
9389
+ margin-top: ${$gapTop}px;
9390
+ }
9391
+ `}
9392
+
9393
+ ${({ $justify }) => $justify &&
9394
+ css `
9395
+ &&& ${ChartLegendContainer} {
9396
+ justify-content: ${$justify};
9397
+ }
9398
+ `}
9399
+
9400
+ ${plotInsetsMixin};
9401
+ ${awaitingPlotMixin};
9402
+ `;
9403
+ /**
9404
+ * Колонка «график + подпись оси X» для легенды сбоку (`twoColumns`). Без неё подпись встала бы третьим
9405
+ * элементом ряда рядом с легендой, а не под графиком.
9406
+ */
9407
+ const ChartWithFooter = styled(Flex).withConfig({ displayName: "ChartWithFooter", componentId: "sc-140gv2s" }) `
9408
+ flex-direction: column;
9409
+ flex-wrap: nowrap;
9410
+ min-width: 0;
9411
+
9412
+ ${({ $fill }) => $fill &&
9413
+ css `
9414
+ min-height: 0;
9415
+ `}
9416
+
9417
+ &&& ${ContainerChart} {
9418
+ width: 100%;
9419
+ }
9420
+ `;
9421
+ const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-vj15z6" }) `
9045
9422
  margin-left: 0.5rem;
9046
9423
  white-space: nowrap;
9047
9424
  font-size: 0.75rem;
9048
9425
  `;
9049
- const ContainerValue = styled(Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-aj1rs9" }) `
9426
+ const ContainerValue = styled(Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-1bnfj5v" }) `
9050
9427
  justify-content: flex-end;
9051
9428
  align-items: center;
9052
9429
  flex-wrap: nowrap;
@@ -9065,7 +9442,7 @@ const ContainerValue = styled(Flex).withConfig({ displayName: "ContainerValue",
9065
9442
  width: ${({ column }) => (column ? "100%" : "auto")};
9066
9443
  }
9067
9444
 
9068
- ${ContainerChart}, ${ContainerLegend} {
9445
+ ${ContainerChart}, ${ContainerLegend}, ${ChartWithFooter} {
9069
9446
  width: ${({ column }) => (column ? "100%" : "50%")};
9070
9447
  }
9071
9448
 
@@ -9088,7 +9465,7 @@ const SizeIconMixin = css `
9088
9465
  font-size: ${({ $fontSize }) => $fontSize}px !important;
9089
9466
  }
9090
9467
  `;
9091
- styled(Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-1w9w0bq" }) `
9468
+ styled(Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-18399bz" }) `
9092
9469
  width: auto;
9093
9470
  height: auto;
9094
9471
  margin-bottom: 0.5rem;
@@ -9102,7 +9479,7 @@ const SvgContainerColorMixin$1 = css `
9102
9479
  fill: ${({ $fontColor }) => $fontColor};
9103
9480
  }
9104
9481
  `;
9105
- const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-1qs5gmx" }) `
9482
+ const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-8l0ajh" }) `
9106
9483
  &&& {
9107
9484
  min-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
9108
9485
  max-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
@@ -9114,7 +9491,7 @@ const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", comp
9114
9491
  }
9115
9492
  }
9116
9493
  `;
9117
- const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-1ogv4d3" }) `
9494
+ const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-1yxeknf" }) `
9118
9495
  flex-direction: row;
9119
9496
  flex-wrap: nowrap;
9120
9497
  align-items: center;
@@ -9141,6 +9518,16 @@ const CameraContainer = memo(({ elementConfig, type, renderElement }) => {
9141
9518
  return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, children: [jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsx(ContainerValue, { children: renderElement({ id: "value", wrap: false }) })] }))] }));
9142
9519
  });
9143
9520
 
9521
+ /**
9522
+ * Контекст, через который элемент `chart` сообщает контейнеру, где лежит поле графика.
9523
+ *
9524
+ * По макету подпись оси X и легенда выравниваются по полю графика, а не по краям контейнера.
9525
+ * Ширину шкал d3 вычисляет только при отрисовке, а подвал с подписью и легендой рисует
9526
+ * `ChartContainer` — снаружи элемента. Без контейнера (элемент сам по себе) провайдера нет,
9527
+ * и отчёт просто не отправляется.
9528
+ */
9529
+ const ChartPlotContext = createContext({});
9530
+
9144
9531
  /**
9145
9532
  * Контекст вписывания графика (`options.fill` контейнера Chart).
9146
9533
  *
@@ -9150,14 +9537,40 @@ const CameraContainer = memo(({ elementConfig, type, renderElement }) => {
9150
9537
  */
9151
9538
  const FillContext = createContext({ fill: false, fitHeight: false });
9152
9539
 
9540
+ const isSameInsets = (prev, next) => !!prev && prev.left === next.left && prev.right === next.right && prev.width === next.width;
9153
9541
  const ChartContainer = memo(({ elementConfig, isVisible, type, renderElement }) => {
9154
9542
  const { options, children } = elementConfig || {};
9155
- const { twoColumns, hideEmpty, height } = options || {};
9543
+ const { twoColumns, hideEmpty, height, legendInline } = options || {};
9156
9544
  const fill = options?.fill;
9157
9545
  const fillContextValue = useMemo(() => ({ fill: !!fill, fitHeight: !!fill && height != null }), [fill, height]);
9546
+ // Поле графика отчитывается после каждой отрисовки d3; одинаковые значения не пишем в стейт,
9547
+ // иначе отчёт и ре-рендер гоняли бы друг друга.
9548
+ const [plotInsets, setPlotInsets] = useState(null);
9549
+ const reportPlotInsets = useCallback((next) => setPlotInsets(prev => (isSameInsets(prev, next) ? prev : next)), []);
9550
+ const plotContextValue = useMemo(() => ({ reportPlotInsets }), [reportPlotInsets]);
9158
9551
  const aliasElement = children.find(child => child.id === "alias");
9159
9552
  const chartElement = children.find(child => child.id === "chart");
9160
9553
  const legendElement = children.find(child => child.id === "legend");
9554
+ const isLineChart = chartElement?.options?.chartType === "line";
9555
+ const xAxisTitle = chartElement?.options?.axis?.x?.title;
9556
+ const footerJustify = useMemo(() => CHART_AXIS_ALIGN_JUSTIFY[chartElement?.options?.axis?.x?.align ?? "center"], [chartElement?.options?.axis?.x?.align]);
9557
+ // Легенда встаёт в строку с подписью оси X только когда та есть и легенда не вынесена вбок.
9558
+ const isLegendInline = useMemo(() => !!legendInline && !twoColumns && !!xAxisTitle, [legendInline, twoColumns, xAxisTitle]);
9559
+ // По макету подпись оси X и легенда у линейного графика выравниваются по полю графика, а не по
9560
+ // контейнеру. Легенда сбоку (twoColumns) живёт в своей колонке и в выравнивании не участвует.
9561
+ const footerInsets = useMemo(() => (isLineChart ? plotInsets : null), [isLineChart, plotInsets]);
9562
+ const isPlotLegend = isLineChart && !twoColumns;
9563
+ // Положение поля становится известно только после отрисовки: LineChart рисует d3 в useEffect, уже
9564
+ // после первого кадра. До отчёта подвал встал бы по краю контейнера и затем прыгнул на место, поэтому
9565
+ // прячем его — место под ним сохраняется, а появляется он вместе с линиями графика.
9566
+ const isAwaitingPlot = isLineChart && !plotInsets;
9567
+ // Пока подпись оси X не задана, легенда выравнивается прежней опцией `center` (по умолчанию — к левой
9568
+ // границе поля графика); с подписью — по ней, иначе подпись и легенда разъезжаются по разным краям.
9569
+ const legendJustify = useMemo(() => {
9570
+ if (xAxisTitle)
9571
+ return footerJustify;
9572
+ return legendElement?.options?.center ? "center" : "flex-start";
9573
+ }, [xAxisTitle, footerJustify, legendElement?.options?.center]);
9161
9574
  const { root, body } = useContainerRoot({ elementConfig });
9162
9575
  const { data, loading } = useChartData({
9163
9576
  element: chartElement,
@@ -9169,7 +9582,12 @@ const ChartContainer = memo(({ elementConfig, isVisible, type, renderElement })
9169
9582
  const hasItems = !!data[0]?.items?.length;
9170
9583
  if (!loading && !hasItems && hideEmpty)
9171
9584
  return null;
9172
- return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, isColumn: true, children: [aliasElement && jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsx(ContainerValue, { column: !twoColumns, alignItems: twoColumns && fill ? "stretch" : "center", "$fill": !!fill, children: hasItems ? (jsxs(Fragment$1, { children: [jsx(ContainerChart, { "$fill": !!fill, children: jsx(FillContext.Provider, { value: fillContextValue, children: renderElement({ id: "chart" }) }) }), jsx(ContainerLegend, { justifyContent: legendElement?.options?.center ? "center" : "flex-start", children: renderElement({ id: "legend" }) })] })) : (jsx(Fragment$1, { children: "\u2014" })) })] }))] }));
9585
+ const chartSlot = (jsx(ContainerChart, { "$fill": !!fill, children: jsx(FillContext.Provider, { value: fillContextValue, children: jsx(ChartPlotContext.Provider, { value: plotContextValue, children: renderElement({ id: "chart" }) }) }) }));
9586
+ const footer = xAxisTitle ? (jsxs(ChartFooter, { "$align": footerJustify, "$insets": footerInsets, "$offsetTop": isLineChart ? X_AXIS_TITLE_GAP - LINE_CHART_X_LABELS_BLANK : 0, "$awaitingPlot": isAwaitingPlot, children: [jsx(ChartXAxisTitle, { children: xAxisTitle }), isLegendInline && jsx(ContainerLegend, { children: renderElement({ id: "legend" }) })] })) : null;
9587
+ // Легенда линейного графика: под подписью оси X — через X_AXIS_TITLE_LEGEND_GAP, без подписи — на её
9588
+ // месте, вплотную под подписями делений оси X. У остальных графиков — прежняя раскладка общей легенды.
9589
+ const legendRow = isLegendInline ? null : (jsx(ChartLegendRow, { justifyContent: legendJustify, "$insets": isPlotLegend ? footerInsets : null, "$gapTop": isPlotLegend ? (xAxisTitle ? X_AXIS_TITLE_LEGEND_GAP : 0) : undefined, "$offsetTop": isPlotLegend && !xAxisTitle ? X_AXIS_TITLE_GAP - LINE_CHART_X_LABELS_BLANK : undefined, "$justify": isPlotLegend ? legendJustify : undefined, "$awaitingPlot": isPlotLegend && isAwaitingPlot, children: renderElement({ id: "legend" }) }));
9590
+ return (jsxs(ContainerRoot, { ...root, children: [jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxs(Container, { ...body, isColumn: true, children: [aliasElement && jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsx(ContainerValue, { column: !twoColumns, alignItems: twoColumns && fill ? "stretch" : "center", "$fill": !!fill, children: hasItems ? (jsxs(Fragment$1, { children: [twoColumns && footer ? (jsxs(ChartWithFooter, { "$fill": !!fill, children: [chartSlot, footer] })) : (jsxs(Fragment$1, { children: [chartSlot, footer] })), legendRow] })) : (jsx(Fragment$1, { children: "\u2014" })) })] }))] }));
9173
9591
  });
9174
9592
 
9175
9593
  const InnerContainerWrapper = styled.div.withConfig({ displayName: "InnerContainerWrapper", componentId: "sc-1a1fl7q" }) `
@@ -9527,6 +9945,7 @@ const DEFAULT_BAR_CHART_HEIGHT = 100;
9527
9945
  const BAR_CHART_HEADER_HEIGHT = 40;
9528
9946
  const BAR_CHART_FOOTER_MARGIN = 16;
9529
9947
  const LINE_CHART_LABEL_MARGIN = 20;
9948
+ const LINE_CHART_X_AXIS_PADDING = 15;
9530
9949
  const DEFAULT_BAR_CHART_MARGINS = {
9531
9950
  top: 0,
9532
9951
  left: 0,
@@ -15190,9 +15609,9 @@ const ElementImage = memo(({ type, elementConfig }) => {
15190
15609
  });
15191
15610
 
15192
15611
  const ElementLegend = memo(({ type, element, elementConfig }) => {
15193
- const { attributes, dataSources, expandedContainers } = useWidgetContext(type);
15612
+ const { attributes, expandedContainers } = useWidgetContext(type);
15194
15613
  const { options } = elementConfig || {};
15195
- const { twoColumns, chartId, relatedDataSources, fontSize } = options || {};
15614
+ const { twoColumns, column, chartId, relatedDataSources, fontSize } = options || {};
15196
15615
  const chartElement = useMemo(() => returnFound(element?.children, { id: chartId }), [chartId, element?.children]);
15197
15616
  const { data, loading } = useChartData({
15198
15617
  element: chartElement,
@@ -15203,10 +15622,10 @@ const ElementLegend = memo(({ type, element, elementConfig }) => {
15203
15622
  if (!data[0]?.items?.length)
15204
15623
  return [];
15205
15624
  if (chartElement?.options?.chartType === "line") {
15206
- return axes.map(({ alias, axisColor }) => {
15625
+ return axes.map(related => {
15207
15626
  return {
15208
- name: alias,
15209
- color: axisColor,
15627
+ name: related.alias,
15628
+ color: resolveAxis(related).color,
15210
15629
  };
15211
15630
  });
15212
15631
  }
@@ -15220,7 +15639,7 @@ const ElementLegend = memo(({ type, element, elementConfig }) => {
15220
15639
  });
15221
15640
  }, [data, chartElement?.options?.chartType, relatedDataSources, axes, attributes]);
15222
15641
  return !chartElement?.options?.expanded ||
15223
- expandedContainers?.[chartElement.id] ? (jsx(ChartLegend, { data: legendData, loading: loading, chartElement: chartElement, twoColumns: twoColumns, fontSize: fontSize, type: type })) : null;
15642
+ expandedContainers?.[chartElement.id] ? (jsx(ChartLegend, { data: legendData, loading: loading, chartElement: chartElement, twoColumns: twoColumns, column: column, fontSize: fontSize, type: type })) : null;
15224
15643
  });
15225
15644
 
15226
15645
  const ExternalLink = styled(IconButton).attrs(() => ({
@@ -16788,7 +17207,11 @@ const getDataFromRelatedFeatures = ({ t, config, filters, relatedConfig, dataSou
16788
17207
  if (isOtherSliced) {
16789
17208
  data = data.slice(0, config?.options?.otherItems);
16790
17209
  }
16791
- const gradientArray = relatedConfig.chartAxis
17210
+ // Источник чарта отличается от источника фильтра наличием осей: BarChartFilter собирает
17211
+ // relatedConfig вручную и оси не задаёт, потому что ему нужна исходная палитра, а не растянутая
17212
+ // под число элементов. Гейт смотрит и на устаревший chartAxis — иначе у старых конфигов
17213
+ // молча поехали бы цвета столбцов и долей.
17214
+ const gradientArray = hasAxisConfig(relatedConfig)
16792
17215
  ? stretchPalette(colors, data.length)
16793
17216
  : colors;
16794
17217
  const filter = getConfigFilter(relatedConfig?.filterName, filters);
@@ -18854,6 +19277,15 @@ const StackBar = ({ data, filterName, type, alias, options, fill, renderElement,
18854
19277
  return (jsxs(Fragment$1, { children: [(alias || showTotal) && (jsxs(StackBarHeader, { children: [jsx(StackBarAlias, { children: renderElement({ id: "alias" }) }), showTotal && (jsxs(StackBarTotal, { children: [jsx(StackBarValue, { children: formatValue(total) }), !!units && jsx(StackBarUnits, { children: units })] }))] })), groupTooltip ? (jsx(Tooltip$1, { placement: "top", arrow: true, content: renderGroupTooltip, children: ref => (jsx(StackBarContainer, { ref: ref, "$fill": fill, children: renderItems })) })) : (jsx(StackBarContainer, { "$fill": fill, children: renderItems }))] }));
18855
19278
  };
18856
19279
 
19280
+ /** Горизонтальные подписи осей над телом графика: левая прижата влево, правая — вправо. */
19281
+ const AxisTitlesTop = ({ left, right, neutralColor }) => (jsxs(AxisTitlesRow, { children: [jsx(AxisTitleTop, { "$color": left?.color || neutralColor, title: left?.title, children: left?.title }), jsx(AxisTitleTop, { "$color": right?.color || neutralColor, title: right?.title, children: right?.title })] }));
19282
+ /** Вертикальная подпись оси сбоку от тела графика. Без подписи ничего не рендерит. */
19283
+ const AxisTitleSideSlot = ({ side, axisTitle, neutralColor, plotTop, plotBottom, }) => {
19284
+ if (!axisTitle)
19285
+ return null;
19286
+ return (jsx(AxisTitleSideColumn, { "$side": side, "$plotTop": plotTop, "$plotBottom": plotBottom, children: jsx(AxisTitleSide, { "$side": side, "$color": axisTitle.color || neutralColor, title: axisTitle.title, children: axisTitle.title }) }));
19287
+ };
19288
+
18857
19289
  const Chart = memo(({ config, element, elementConfig, type, renderElement }) => {
18858
19290
  const theme = useTheme();
18859
19291
  const { palette } = theme;
@@ -18874,12 +19306,18 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
18874
19306
  const radius = options?.radius || DEFAULT_BARCHART_RADIUS;
18875
19307
  const padding = options?.padding || DEFAULT_BAR_PADDING;
18876
19308
  const fontColor = options?.fontColor || palette.iconDisabled;
19309
+ // Нейтральная подпись оси — тем же цветом, что и значения шкалы (на макете у них один стиль).
19310
+ const axisTitleColor = options?.fontColor || palette.textDisabled;
18877
19311
  const angle = options?.angle || DEFAULT_CHART_ANGLE;
18878
19312
  const barWidth = options?.barWidth || DEFAULT_BAR_WIDTH;
18879
19313
  const cornerRadius = options?.cornerRadius || 0;
18880
19314
  const relatedAttributes = useMemo(() => relatedDataSources || [], [relatedDataSources]);
18881
- const axes = useMemo(() => relatedAttributes.filter(({ chartAxis }) => chartAxis === "y"), [relatedAttributes]);
19315
+ const axes = useMemo(() => relatedAttributes.filter(isValueAxis), [relatedAttributes]);
18882
19316
  const filterName = getChartFilterName(relatedDataSources);
19317
+ const { axisTitles, hasTopTitles, hasSideTitles, axisTitlesHeight, hideLeftAxis, hideRightAxis, } = useChartAxisTitles({ options, axes, isLineChart });
19318
+ // Ширина вертикальных подписей зависит от числа строк текста, поэтому тело между ними меряем
19319
+ // по факту, а не вычитаем константу.
19320
+ const [lineBodyRef, { width: measuredBodyWidth }] = useResizeBox(hasSideTitles);
18883
19321
  const isRelated = !!relatedAttributes?.length;
18884
19322
  const { data, loading } = useChartData({ element, type });
18885
19323
  const totalWidthFromBarWidth = (data[0]?.items?.length || 0) * (barWidth + padding);
@@ -18907,17 +19345,54 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
18907
19345
  const chartHeight = useMemo(() => (fitsHeight && measuredHeight ? measuredHeight : height), [fitsHeight, measuredHeight, height]);
18908
19346
  // Круглый график вписываем по короткой стороне (object-fit: contain), центрируя в ячейке.
18909
19347
  const pieSide = useMemo(() => (fitsHeight && measuredHeight ? Math.min(chartWidth, measuredHeight) : chartWidth), [fitsHeight, measuredHeight, chartWidth]);
19348
+ // Тело линейного графика — это ячейка за вычетом подписей осей. По высоте вычитаем только в
19349
+ // fill: вне fill chartHeight означает высоту самого тела, и обёртка честно растёт на подписи.
19350
+ const lineBodyWidth = useMemo(() => {
19351
+ if (!hasSideTitles)
19352
+ return chartWidth;
19353
+ if (measuredBodyWidth)
19354
+ return measuredBodyWidth;
19355
+ // До первого замера — оценка: одна строка текста и зазор на каждую подпись.
19356
+ const sideTitlesCount = Number(!!axisTitles.left) + Number(!!axisTitles.right);
19357
+ return Math.max(chartWidth - sideTitlesCount * (AXIS_TITLE_LINE_HEIGHT + AXIS_TITLE_GAP), 0);
19358
+ }, [hasSideTitles, measuredBodyWidth, chartWidth, axisTitles]);
19359
+ const lineBodyHeight = useMemo(() => (fitsHeight ? Math.max(chartHeight - axisTitlesHeight, 0) : chartHeight), [fitsHeight, chartHeight, axisTitlesHeight]);
19360
+ // Обёртка выше тела на строку горизонтальных подписей. В fill вся высота уже отмерена ячейкой,
19361
+ // поэтому прибавлять нечего — иначе тело вылезло бы за нижнюю границу.
19362
+ const lineWrapperHeight = useMemo(() => (fitsHeight ? chartHeight : chartHeight + axisTitlesHeight), [fitsHeight, chartHeight, axisTitlesHeight]);
18910
19363
  const [customize] = useChartChange({
18911
19364
  dataSources: config.dataSources,
18912
19365
  chartId: elementConfig?.id,
18913
- width: chartWidth,
18914
- height: chartHeight,
19366
+ width: lineBodyWidth,
19367
+ height: lineBodyHeight,
18915
19368
  fontColor,
18916
19369
  relatedAttributes,
18917
19370
  defaultColor: primaryColor,
18918
19371
  markers: configMarkers,
18919
19372
  showMarkers,
18920
19373
  });
19374
+ const { formatLeftTick, formatRightTick } = useChartAxisTickFormat({
19375
+ dataSources: config.dataSources,
19376
+ relatedAttributes,
19377
+ });
19378
+ const { reportPlotInsets } = useContext(ChartPlotContext);
19379
+ // Положение поля графика известно только после отрисовки: ширину шкал вычисляет d3. Смещение тела
19380
+ // берём из offset-размеров, а не из getBoundingClientRect: они не искажаются масштабом превью и
19381
+ // transform предков, а координаты шкалы всегда в пикселях SVG.
19382
+ const customizeLine = useCallback(args => {
19383
+ customize?.(args);
19384
+ const body = lineBodyRef.current;
19385
+ const layout = body?.parentElement?.parentElement;
19386
+ if (!reportPlotInsets || !body || !layout)
19387
+ return;
19388
+ const bodyLeft = body.offsetLeft - layout.offsetLeft;
19389
+ const [plotLeft, plotRight] = args.xScale.range();
19390
+ reportPlotInsets({
19391
+ left: Math.round(bodyLeft + plotLeft),
19392
+ right: Math.round(layout.offsetWidth - bodyLeft - plotRight),
19393
+ width: layout.offsetWidth,
19394
+ });
19395
+ }, [customize, reportPlotInsets, lineBodyRef]);
18921
19396
  const { formatFilterColor, onFilter } = useWidgetFilters(type, filterName, data?.[0]?.items);
18922
19397
  const formattedAttributes = useMemo(() => getFormattedAttributes(t, (data[0]?.items || []), attributes, element), [attributes, data, element, t]);
18923
19398
  const totalSum = useMemo(() => isRelated
@@ -18976,6 +19451,17 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
18976
19451
  left: markersMargin,
18977
19452
  };
18978
19453
  }, [showMarkers, markers?.length]);
19454
+ // Значения шкалы барчарта d3 центрирует на делении, и крайние деления наполовину выступают за поле.
19455
+ // При нулевом поле SVG выступ уходит за SVG, где его срезает BarChartContainer (overflow-x: hidden,
19456
+ // отчего и по вертикали содержимое не visible). Место под выступ резервируем внутри SVG — внешние
19457
+ // размеры обёрток не меняются. Без значений шкалы (showLabels) выступать нечему.
19458
+ const barMargin = useMemo(() => showLabels
19459
+ ? {
19460
+ ...margin,
19461
+ top: Math.max(margin.top, Y_AXIS_LABEL_OVERHANG),
19462
+ bottom: Math.max(margin.bottom, Y_AXIS_LABEL_OVERHANG),
19463
+ }
19464
+ : margin, [showLabels, margin]);
18979
19465
  const formatTooltipName = useCallback((name) => isRelated ? name : tooltipNameFromAttributes(name, formattedAttributes), [formattedAttributes, isRelated]);
18980
19466
  const formatTooltipValue = useCallback((value, name) => isRelated
18981
19467
  ? tooltipValueFromRelatedFeatures(t, value, relatedAttributes, data[0]?.layerInfo)
@@ -19002,26 +19488,42 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
19002
19488
  attributeUnits: item.attributeUnits,
19003
19489
  dataSourceName: item.dataSourceName,
19004
19490
  stroke: item.color || primaryColor,
19491
+ axis: item.axisSide ?? "left",
19005
19492
  values: item.items
19006
19493
  ?.filter(({ value }) => !isEmptyValue(value))
19007
19494
  ?.map(({ value }) => Number(value)),
19008
19495
  }));
19009
- const isHidedY = axes.every(({ hideAxis }) => hideAxis);
19010
- const chartValues = lineChartData
19011
- .flat()
19496
+ const isHidedY = hideLeftAxis && hideRightAxis;
19497
+ const valuesOfSide = (side) => lineChartData
19498
+ .filter(({ axis }) => axis === side)
19012
19499
  .map(({ values }) => values)
19013
19500
  .flat();
19501
+ const leftValues = valuesOfSide("left");
19502
+ const rightValues = valuesOfSide("right");
19014
19503
  // Резервируем нижнюю полосу под подписи оси X: без неё ось прижата к нижней границе,
19015
19504
  // подписи (сдвинутые на +16px) вылезают за тело и режутся overflow: hidden у ChartFillMeasure.
19505
+ // Сверху резервируем выступ верхнего деления шкалы: оно центрировано на линии сетки и при нулевом
19506
+ // поле наполовину уходит за SVG, где в fill его срезает overflow: hidden у ChartFillMeasure.
19507
+ const topOverhang = Math.max(Y_AXIS_LABEL_OVERHANG - margin.top, 0);
19016
19508
  const lineMargin = {
19017
19509
  ...margin,
19510
+ top: margin.top + topOverhang,
19018
19511
  bottom: Math.max(margin.bottom, LINE_CHART_LABEL_MARGIN),
19019
19512
  };
19020
- return (jsxs(AnyChartWrapper, { height: chartHeight, children: [jsx(LineChartStyles, {}), jsx(LineChart, { data: lineChartData, labels: labels, markers: markers, width: chartWidth, height: chartHeight, xAxisPadding: 15, yAxisPadding: 0, min: Math.min(...chartValues), customYAxisSelection: yAxis => {
19021
- if (isHidedY) {
19022
- yAxis.remove();
19023
- }
19024
- }, customYAxis: yAxis => yAxis.ticks(4), renderTooltip: renderLineChartTooltip, customize: customize, dotSnapping: dotSnapping, dynamicTooltipEnable: true, stackedTooltip: true, tooltipClassName: "dashboardLineChartTooltip", drawGridX: !isHidedY, margin: lineMargin })] }));
19513
+ // draw.ts вычитает верхнее поле и из нижней границы поля графика — компенсируем добавленное,
19514
+ // чтобы низ поля и зазор до подписей оси X остались на месте.
19515
+ const lineXAxisPadding = LINE_CHART_X_AXIS_PADDING - topOverhang;
19516
+ // Колонки вертикальных подписей повторяют поля SVG, чтобы подпись центрировалась по полю
19517
+ // графика, а не по всей высоте вместе с подписями оси X.
19518
+ const plotTop = lineMargin.top;
19519
+ const plotBottom = lineMargin.top + lineMargin.bottom + lineXAxisPadding;
19520
+ return (jsxs(AnyChartWrapper, { height: lineWrapperHeight, children: [jsx(LineChartStyles, {}), jsxs(LineChartLayout, { children: [hasTopTitles && (jsx(AxisTitlesTop, { left: axisTitles.left, right: axisTitles.right, neutralColor: axisTitleColor })), jsxs(LineChartRow, { children: [hasSideTitles && (jsx(AxisTitleSideSlot, { side: "left", axisTitle: axisTitles.left, neutralColor: axisTitleColor, plotTop: plotTop, plotBottom: plotBottom })), jsx(LineChartBody, { ref: lineBodyRef, children: jsx(LineChart, { data: lineChartData, labels: labels, markers: markers, width: lineBodyWidth, height: lineBodyHeight, xAxisPadding: lineXAxisPadding, yAxisPadding: 0, min: leftValues.length ? Math.min(...leftValues) : undefined, rightMin: rightValues.length ? Math.min(...rightValues) : undefined, customYAxisSelection: yAxis => {
19521
+ // Обе оси приходят в один колбэк, поэтому сторону определяем по классу.
19522
+ const isRight = yAxis.classed(lineChartClassNames.lineChartYScaleRight);
19523
+ if (isRight ? hideRightAxis : hideLeftAxis) {
19524
+ yAxis.remove();
19525
+ }
19526
+ }, customYAxis: yAxis => yAxis.ticks(LINE_CHART_Y_TICKS).tickFormat(formatLeftTick), customYAxisRight: yAxis => yAxis.ticks(LINE_CHART_Y_TICKS).tickFormat(formatRightTick), renderTooltip: renderLineChartTooltip, customize: customizeLine, dotSnapping: dotSnapping, dynamicTooltipEnable: true, stackedTooltip: true, tooltipClassName: "dashboardLineChartTooltip", drawGridX: !isHidedY, margin: lineMargin }) }), hasSideTitles && (jsx(AxisTitleSideSlot, { side: "right", axisTitle: axisTitles.right, neutralColor: axisTitleColor, plotTop: plotTop, plotBottom: plotBottom }))] })] })] }));
19025
19527
  }
19026
19528
  if (isStackBar) {
19027
19529
  // Вне fill chartHeight — высота самой полосы, и обёртка честно становится выше на строку
@@ -19051,7 +19553,7 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
19051
19553
  const barRadius = options?.cornerRadius ?? DEFAULT_BARCHART_RADIUS;
19052
19554
  bars.attr("rx", barRadius);
19053
19555
  bars.attr("ry", barRadius);
19054
- }, margin: margin, xAxisPadding: 0, yAxisPadding: 0, formatTooltipValue: formatTooltipValue, formatTooltipName: formatTooltipName, hideTooltipGroupName: true, dynamicTooltipEnable: true, isBarTooltip: true, onBarClick: filterName ? item => onFilter(item.name) : undefined }) }) }));
19556
+ }, margin: barMargin, xAxisPadding: 0, yAxisPadding: 0, formatTooltipValue: formatTooltipValue, formatTooltipName: formatTooltipName, hideTooltipGroupName: true, dynamicTooltipEnable: true, isBarTooltip: true, onBarClick: filterName ? item => onFilter(item.name) : undefined }) }) }));
19055
19557
  }, [
19056
19558
  customXAxisBottom,
19057
19559
  customYAxisLeft,
@@ -19067,6 +19569,15 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
19067
19569
  formatFilterColor,
19068
19570
  chartWidth,
19069
19571
  chartHeight,
19572
+ lineBodyWidth,
19573
+ lineBodyHeight,
19574
+ lineWrapperHeight,
19575
+ axisTitles,
19576
+ hasTopTitles,
19577
+ hasSideTitles,
19578
+ axisTitleColor,
19579
+ hideLeftAxis,
19580
+ hideRightAxis,
19070
19581
  fitsHeight,
19071
19582
  pieSide,
19072
19583
  fill,
@@ -19076,11 +19587,14 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
19076
19587
  formatTooltipValue,
19077
19588
  formatTooltipName,
19078
19589
  filterName,
19079
- axes,
19080
19590
  labels,
19081
19591
  margin,
19592
+ barMargin,
19082
19593
  renderLineChartTooltip,
19083
- customize,
19594
+ customizeLine,
19595
+ lineBodyRef,
19596
+ formatLeftTick,
19597
+ formatRightTick,
19084
19598
  primaryColor,
19085
19599
  type,
19086
19600
  elementConfig?.children,
@@ -19097,14 +19611,14 @@ const Chart = memo(({ config, element, elementConfig, type, renderElement }) =>
19097
19611
  ]);
19098
19612
  if (!isVisibleContainer(id, expandable, expanded, expandedContainers))
19099
19613
  return null;
19100
- const chartBody = (jsxs(ChartWrapper, { width: isPieChart && fill ? pieSide : width, height: isStackBar ? "auto" : isPieChart ? (fill ? pieSide : width) : chartHeight, column: column, loading: loading, children: [jsx(ChartTooltipStyles, {}), renderChart] }));
19614
+ const chartBody = (jsxs(ChartWrapper, { width: isPieChart && fill ? pieSide : width, height: isStackBar ? "auto" : isPieChart ? (fill ? pieSide : width) : isLineChart ? lineWrapperHeight : chartHeight, column: column, loading: loading, children: [jsx(ChartTooltipStyles, {}), renderChart] }));
19101
19615
  if (!fill)
19102
19616
  return chartBody;
19103
19617
  // fill: тело центрируется в измеряемой обёртке, ref которой питает ResizeObserver обеими осями.
19104
19618
  return jsx(ChartFillMeasure, { ref: chartRef, children: chartBody });
19105
19619
  });
19106
19620
 
19107
- const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, loading }) => {
19621
+ const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, column, loading }) => {
19108
19622
  const { t } = useGlobalContext();
19109
19623
  const { sliceItems, showMore, onShowMore } = useShownOtherItems(chartElement?.options);
19110
19624
  const axes = useMemo(() => getChartAxes(chartElement), [chartElement]);
@@ -19113,7 +19627,7 @@ const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, loading }
19113
19627
  const showMoreBtn = !!shownItems && data.length > shownItems;
19114
19628
  if (!loading && !data?.length && !!hideEmpty)
19115
19629
  return null;
19116
- return (jsxs(Flex, { flexDirection: "column", opacity: loading ? FILTERED_VALUE_OPACITY / 100 : 1, children: [jsx(ChartLegendContainer, { twoColumns: twoColumns, children: sliceItems(data)?.map(({ name, color }, index) => (jsxs(ChartLegendItem, { hasAnyFilter: hasAnyFilter, isFiltered: isFiltered(name), onClick: axes?.[0]?.filterName ? () => onFilter(name) : undefined, children: [jsx(ChartLegendColor, { color: color }), jsx(ChartLegendName, { "$fontSize": fontSize, "$fontColor": fontColor, children: name })] }, index))) }), showMoreBtn && (jsx(LegendToggler, { onClick: onShowMore, toggled: showMore, children: t("other", { ns: "dashboard", defaultValue: "Другое" }) }))] }));
19630
+ return (jsxs(Flex, { flexDirection: "column", opacity: loading ? FILTERED_VALUE_OPACITY / 100 : 1, children: [jsx(ChartLegendContainer, { twoColumns: twoColumns, "$column": column, children: sliceItems(data)?.map(({ name, color }, index) => (jsxs(ChartLegendItem, { hasAnyFilter: hasAnyFilter, isFiltered: isFiltered(name), onClick: axes?.[0]?.filterName ? () => onFilter(name) : undefined, children: [jsx(ChartLegendColor, { color: color }), jsx(ChartLegendName, { "$fontSize": fontSize, "$fontColor": fontColor, children: name })] }, index))) }), showMoreBtn && (jsx(LegendToggler, { onClick: onShowMore, toggled: showMore, children: t("other", { ns: "dashboard", defaultValue: "Другое" }) }))] }));
19117
19631
  };
19118
19632
 
19119
19633
  const ContainerChildren = memo(({ type, items, isColumn, isMain, renderElement }) => {
@@ -19968,5 +20482,5 @@ const DEFAULT_HEATMAP_STYLE = {
19968
20482
  ],
19969
20483
  };
19970
20484
 
19971
- export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROJECT_ALIAS_PROP, PROJECT_FILTER_NAME, PROJECT_NAME_PROP, PROJECT_PROPS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RESIZE_HANDLE_ATTR, ResizeHandle, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, VoteContainer, WidgetType, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, colorToHex, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getModalsDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProjectValue, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getShownItemsLimit, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRootOwningContainer, isTreeFilterValue, isVisibleContainer, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, removeTracks, replaceNodesByIds, rgbToHex, roundFr, roundTotalSum, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAttachmentsView, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartChange, useChartData, useConfigDataSources, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useResizeDrag, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
20485
+ export { ALIGNMENTS, ALIGN_ITEMS, ATTRIBUTE_ICON_ELEMENT_TYPES, AXIS_TITLE_GAP, AXIS_TITLE_LINE_HEIGHT, AXIS_TITLE_TOP_GRID_GAP, AXIS_TITLE_TOP_HEIGHT, AddButtonRow, AddFeatureButton, AddFeatureContainer, AlertIconContainer, AttachmentContainer, AttributeGalleryContainer, AttributeLabel, BASE_CONTAINER_STYLE, BG_IMAGE_SLOT_ID, BaseMapTheme, CHART_AXIS_ALIGN_JUSTIFY, CHART_TYPES, COMPACT_FRACTION_DIGITS, CONFIG_PAGES_ID, CONFIG_PAGE_ID, CONTAINERS_GROUP_DEFAULTS, CONTAINER_BODY_ATTRIBUTE, CONTAINER_BODY_FILL_STYLE, CameraContainer, Chart, ChartContainer, ChartLegend, ChartLoading, Container, ContainerBackground, ContainerChildren, ContainerLoading, ContainerRoot, ContainerTemplate, ContainerWrapper, ContainersGroupContainer, DASHBOARD_OVERLAY_Z_INDEX, DEFAULT_ATTRIBUTE_NAME, DEFAULT_BARCHART_RADIUS, DEFAULT_BASE_MAP, DEFAULT_BLUR, DEFAULT_CHART_ANGLE, DEFAULT_CHART_HEIGHT, DEFAULT_CHART_WIDTH, DEFAULT_CIRCLE_RADIUS, DEFAULT_CIRCLE_STROKE_WIDTH, DEFAULT_CIRCLE_STYLE, DEFAULT_COLOR, DEFAULT_DASHBOARD_CONFIG, DEFAULT_DATA_SOURCE_LIMIT, DEFAULT_DROPDOWN_WIDTH, DEFAULT_FILL_EXTRUSION_BASE, DEFAULT_FILL_EXTRUSION_HEIGHT, DEFAULT_FILL_EXTRUSION_STYLE, DEFAULT_FILL_EXTRUSION_VERTICAL_GRADIENT, DEFAULT_FILL_STYLE, DEFAULT_FILTER_PADDING, DEFAULT_GRID_GAP, DEFAULT_HEATMAP_COLOR, DEFAULT_HEATMAP_INTENSITY, DEFAULT_HEATMAP_RADIUS, DEFAULT_HEATMAP_STYLE, DEFAULT_HEATMAP_WEIGHT, DEFAULT_ICON_ANCHOR, DEFAULT_ICON_OVERLAP, DEFAULT_ICON_PADDING, DEFAULT_ICON_ROTATE, DEFAULT_ICON_SIZE, DEFAULT_ID_ATTRIBUTE_NAME, DEFAULT_LAT, DEFAULT_LINE_CAP, DEFAULT_LINE_JOIN, DEFAULT_LINE_STYLE, DEFAULT_LINE_WIDTH, DEFAULT_LNG, DEFAULT_OPACITY, DEFAULT_PAGES_CONFIG, DEFAULT_PIECHART_RADIUS, DEFAULT_SYMBOL_COLOR, DEFAULT_SYMBOL_HALO_COLOR, DEFAULT_SYMBOL_PLACEMENT, DEFAULT_SYMBOL_SPACING, DEFAULT_SYMBOL_STYLE, DEFAULT_TEXT_ANCHOR, DEFAULT_TEXT_JUSTIFY, DEFAULT_TEXT_SIZE, DEFAULT_TEXT_TRANSFORM, DEFAULT_TRACK_FR, DEFAULT_TRANSLATE, DEFAULT_ZOOM, DRAG_THRESHOLD_PX, Dashboard, DashboardCheckbox, DashboardChip, DashboardContent, DashboardContext, DashboardDefaultHeader, DashboardHeader, DashboardLoading, DashboardPlaceholder, DashboardPlaceholderWrap, DashboardProvider, DashboardWrapper, DataSourceContainer, DataSourceError, DataSourceErrorContainer, DataSourceInnerContainer, DataSourceProgressContainer, DateFormat, DefaultAttributesContainer, DefaultHeaderContainer, DefaultHeaderWrapper, DividerContainer, EMPTY_DATA_SOURCE_LAYER_INFO, EXTENT_FILTER_NAME, EditGeometryType, ElementButton, ElementCamera, ElementChart, ElementChips, ElementControl, ElementIcon, ElementImage, ElementLegend, ElementLink, ElementMarkdown, ElementSlideshow, ElementSvg, ElementTable, ElementTooltip, ElementValueWrapper, ExpandableTitle, FEATURE_CARD_DEFAULT_COLORS, FEATURE_CARD_OTHER_COLOR, FILL_SIZE, FILTERED_VALUE_OPACITY, FILTER_PREFIX, FR_PRECISION, FeatureCardBackgroundHeader, FeatureCardButtons, FeatureCardContext, FeatureCardDefaultHeader, FeatureCardHeader, FeatureCardProvider, FeatureCardSlideshowHeader, FeatureCardTitle, FeatureControls, FeatureTitleContainer, FiltersContainer, GEOMETRY_ATTRIBUTE, GEOMETRY_FILTER_NAME, GRID_AUTO_FILL_DEFAULTS, GRID_CELL_ATTR, GRID_CELL_ID_PREFIX, GRID_DRAGGING_ATTR, GRID_DRAG_SOURCE_ATTR, GRID_DROP_TARGET_ATTR, GRID_FILL_DEFAULTS, GRID_ROW_ID_PREFIX, GlobalContext, GlobalProvider, GridRowContainer, HANDLE_BLEED_PX, Header, HeaderContainer, HeaderFontColorMixin, HeaderFrontView, HeaderTemplate, HeaderTitleContainer, HiddenTitleItems, IconContainer, ImageContainer, LEFT_PANEL_HEADER_HEIGHT, LINE_CHART_X_LABELS_BLANK, LINE_CHART_Y_TICKS, Layer, LayerDescription, LayerGroupList, LayerIcon, LayerIconContainer, LayerListContainer, LayerTree, LayersContainer, LayersListWrapper, LinearProgressContainer, LogTerminal, LogoContainer, MAP_VIEW_FILTER_NAMES, MAX_CHART_WIDTH, MAX_TRACKS, MIN_TRACK_PX, MIN_TRACK_RATIO, Map$1 as Map, MapContext, MapProvider, NON_TRACK_SLOT_IDS, NO_CELL_DRAG_SELECTOR, NO_CONTENT_VALUE, NUMERIC_ATTRIBUTE_TYPES, NoLiveSnapshotContainer, OBJECT_FITS, OVERFLOWS, OneColumnContainer, PIE_CHART_TOOLTIP_STYLE, POLL_SUBTASK_INTERVAL_MS, POLL_SUBTASK_TIMEOUT_MS, PROJECT_ALIAS_PROP, PROJECT_FILTER_NAME, PROJECT_NAME_PROP, PROJECT_PROPS, PROVIDER_PREFIX, PageNavigator, PageTitle, PageTitleContainer, PagesContainer, Pagination, PresentationHeader, PresentationHeaderButtons, PresentationHeaderTools, PresentationPanelContainer, PresentationPanelWrapper, PresentationWrapper, ProgressContainer, ProviderPrefix, QUERY_DESCRIPTION_CACHE_TTL, RESIZE_HANDLE_ATTR, ResizeHandle, RoundedBackgroundContainer, SAVE_HOOK_RESULT_DURATION, SERVER_NOTIFICATION_EVENT, STACK_BAR_TOTAL_HEIGHT, ScalingFactor, ServerNotificationsContext, ServerNotificationsProvider, SlideshowContainer, SmallPreviewContainer$1 as SmallPreviewContainer, SmallPreviewControl, SmallPreviewCounter, SmallPreviewImages, SmallPreviewLeft, SmallPreviewRight, StackBar, StructuredDataContainer, SvgImage, TILE_ALIGNMENTS, TIME_ZONE_FORMAT, TITLE_SLOT_IDS, TabsContainer, TextTrim, ThemeName, TitleContainer, TopContainer, TopContainerButtons, TwoColumnContainer, UploadContainer, VIEW_MODES, VoteContainer, WidgetType, X_AXIS_TITLE_GAP, X_AXIS_TITLE_LEGEND_GAP, Y_AXIS_LABEL_OVERHANG, ZOOM_FILTER_NAME, addDataSource, addDataSources, adjustColor, applyFiltersToCondition, applyQueryFilters, applyTreeFilterToCondition, applyVarsToCondition, asAttributeName, asChartId, asContainerId, asDataSourceName, asFilterName, asLayerName, asModalId, asResourceId, asTabId, buildGridTemplate, buildTrackTemplate, checkEqualOrIncludes, checkIsLoading, collectConfigIds, colorToHex, containsNodeId, createConfigLayer, createConfigPage, createGridCell, createGridIdFactory, createGridRow, createNewPageId, createSaveNotificationId, dateOptions, debounce, decimalOpacityToHex, enrichStyleItemsWithIds, enrichStyleModelsWithIds, eqlParametersToPayload, fetchQueryDescription, findAttributeInExpression, findCellContext, formatArea, formatAttributeValue, formatChartRelatedValue, formatConditionValue, formatDataSourceCondition, formatDate$1 as formatDate, formatElementValue, formatLength, formatNumber, formatPolygonMeasure, geometryToEwkt, getActualExtrusionHeight, getAttributeByName, getAttributeConfigurationByName, getAttributeIconElement, getAttributeIconUrl, getAttributeValue, getAttributesConfiguration, getAverageTrackSize, getAxisSide, getChartAxes, getChartFilterName, getChartMarkers, getConfigFilter, getContainerComponent, getDashboardHeader, getDataFromAttributes, getDataFromRelatedFeatures, getDataSource, getDataSourceFilterValue, getDataSourceLayerInfo, getDate, getDefaultConfig, getDisplayTemplateNameFromAttribute, getElementValue, getFeatureAttributes, getFeatureCardHeader, getFilterComponent, getFilterSelectedItems, getFilterValue, getFormattedAttributes, getGradientColors, getImageUrl, getLayerClientStyle, getLayerInfo, getLayerInfoAttribute, getLayerInfoFromDataSources, getLayoutChildren, getMapViewDataSources, getModalsDataSources, getPagesFromConfig, getPagesFromProjectInfo, getProjectValue, getProxyService, getRelatedAttribute, getRenderElement, getResourceUrl, getRootElementId, getSelectedFilterValue, getShownItemsLimit, getSlideshowImages, getStyleAttributes, getSvgUrl, getTemplateNameFromAttribute, getThemeByName, getTotalFromAttributes, getTotalFromRelatedFeatures, getTrackSize, getTrackSizeKey, getTrackSizes, getWrapperSizeStyle, hasAxisConfig, hasContainerBgImage, hexToRgba, isCrossOriginUrl, isEmptyElementValue, isEmptyValue, isFeaturesFilterValue, isFillSize, isFrSize, isGridNode, isHiddenEmptyValue, isHookActive, isLayerService, isNotValidSelectedTab, isNumeric, isObject, isProxyService, isRightAxis, isRootOwningContainer, isTreeFilterValue, isValueAxis, isVisibleContainer, mapNodeById, mergeAttributeConfigurations, metersPerPixel, noMarginMixin, numberOptions, parseFrValue, parseIconNames, parseIconNamesFromClientStyle, pieChartTooltipFromAttributes, pieChartTooltipFromRelatedFeatures, pointOptions, removeDataSource, removeTracks, replaceNodesByIds, resolveAxis, rgbToHex, roundFr, roundTotalSum, setLayoutChildren, sizeCssMixin, sliceShownOtherItems, stretchPalette, timeOptions, toConditionsArray, toCssSize, toFrSize, toPxNumber, toRenderableValue, tooltipNameFromAttributes, tooltipValueFromAttributes, tooltipValueFromRelatedFeatures, transparentizeColor, updateDataSource, useAfterSave, useAppHeight, useAttachmentDownload, useAttachmentItems, useAttachmentPreviewImages, useAttachmentsView, useAutoCompleteControl, useBeforeSave, useBgImageHost, useChartAxisAttributes, useChartAxisTickFormat, useChartAxisTitles, useChartChange, useChartData, useConfigDataSources, useContainerAttributes, useContainerRoot, useCurrentPageLayers, useCustomFeatureSelect, useDashboardHeader, useDataSourceLoading, useDataSources, useDebouncedCallback, useDiffPage, useEditGroupAttributes, useEqualTileWidth, useExpandableContainers, useExportPdf, useFeatureSaveHooks, useFetchImageWithAuth, useFetchWithAuth, useGetConfigLayer, useGlobalContext, useHeaderRender, useHideIfEmptyDataSource, useLayerHiddenAttributes, useLayerParams, useMapContext, useMapDraw, useMapImages, useMaxZoomTo, useProjectDashboardInit, usePythonSandbox, usePythonTask, useRedrawLayer, useRelatedDataSourceAttributes, useRemoteTask, useRenderElement, useResizeBox, useResizeDrag, useSavePrototypeBuilder, useServerNotificationsContext, useShownOtherItems, useToggle, useUpdateDataSource, useVisibleProjectItems, useWidgetConfig, useWidgetContext, useWidgetFilters, useWidgetPage, useWindowResize, useWrapperSize, useZoomToFeatures, useZoomToPoint, withTrackSize };
19972
20486
  //# sourceMappingURL=react.esm.js.map