@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/components/Dashboard/componentTypes.d.ts +3 -3
- package/dist/components/Dashboard/components/Chart/ChartPlotContext.d.ts +24 -0
- package/dist/components/Dashboard/components/Chart/components/AxisTitles.d.ts +23 -0
- package/dist/components/Dashboard/components/Chart/styled.d.ts +33 -0
- package/dist/components/Dashboard/components/ChartLegend/index.d.ts +1 -0
- package/dist/components/Dashboard/components/ChartLegend/styled.d.ts +1 -0
- package/dist/components/Dashboard/constants.d.ts +37 -1
- package/dist/components/Dashboard/containers/ChartContainer/types.d.ts +3 -1
- package/dist/components/Dashboard/containers/FiltersContainer/constants.d.ts +1 -0
- package/dist/components/Dashboard/containers/styled.d.ts +38 -0
- package/dist/components/Dashboard/hooks/index.d.ts +3 -0
- package/dist/components/Dashboard/hooks/useChartAxisAttributes.d.ts +19 -0
- package/dist/components/Dashboard/hooks/useChartAxisTickFormat.d.ts +18 -0
- package/dist/components/Dashboard/hooks/useChartAxisTitles.d.ts +32 -0
- package/dist/components/Dashboard/types.d.ts +59 -1
- package/dist/components/Dashboard/utils/chartAxis.d.ts +29 -0
- package/dist/components/Dashboard/utils/index.d.ts +1 -0
- package/dist/index.js +617 -85
- package/dist/index.js.map +1 -1
- package/dist/react.esm.js +600 -86
- package/dist/react.esm.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -3358,6 +3358,46 @@ const MAX_CHART_WIDTH = 300;
|
|
|
3358
3358
|
const DEFAULT_CHART_ANGLE = 4;
|
|
3359
3359
|
const DEFAULT_CHART_HEIGHT = 90;
|
|
3360
3360
|
const STACK_BAR_TOTAL_HEIGHT = 20;
|
|
3361
|
+
/** Высота строки текста подписи оси (шрифт 10px) — по макету. */
|
|
3362
|
+
const AXIS_TITLE_LINE_HEIGHT = 12;
|
|
3363
|
+
/** Зазор между подписью оси и значениями шкалы — по макету. */
|
|
3364
|
+
const AXIS_TITLE_GAP = 4;
|
|
3365
|
+
/**
|
|
3366
|
+
* Поле SVG графика под выступ крайних делений шкалы значений.
|
|
3367
|
+
*
|
|
3368
|
+
* d3 центрирует подпись деления на его линии (`dy: 0.32em`), поэтому у крайнего деления половина подписи
|
|
3369
|
+
* выходит за поле графика. Если поле вплотную к краю SVG, половина оказывается за SVG, и её срезает
|
|
3370
|
+
* обёртка: у линейного графика в fill — `ChartFillMeasure` (`overflow: hidden`), у барчарта всегда —
|
|
3371
|
+
* `BarChartContainer` (`overflow-x: hidden`, отчего и по вертикали содержимое не `visible`).
|
|
3372
|
+
*/
|
|
3373
|
+
const Y_AXIS_LABEL_OVERHANG = 6;
|
|
3374
|
+
/** Зазор от строки горизонтальных подписей осей до верхней линии сетки — по макету. */
|
|
3375
|
+
const AXIS_TITLE_TOP_GRID_GAP = 9;
|
|
3376
|
+
/** Число делений шкал линейного графика; у левой и правой шкал оно общее, чтобы совпадала сетка. */
|
|
3377
|
+
const LINE_CHART_Y_TICKS = 4;
|
|
3378
|
+
/**
|
|
3379
|
+
* Высота строки горизонтальных подписей осей (`axis.titlePosition: "top"`). Вместе с верхним полем SVG
|
|
3380
|
+
* (Y_AXIS_LABEL_OVERHANG) даёт строку текста и 9px до верхней линии сетки: деление центрировано на
|
|
3381
|
+
* линии и выступает над ней на ~5px, так что между подписью и значением шкалы остаются те же 4px,
|
|
3382
|
+
* что и у вертикальной подписи.
|
|
3383
|
+
*/
|
|
3384
|
+
const AXIS_TITLE_TOP_HEIGHT = AXIS_TITLE_LINE_HEIGHT + AXIS_TITLE_TOP_GRID_GAP - Y_AXIS_LABEL_OVERHANG;
|
|
3385
|
+
/**
|
|
3386
|
+
* Пустая полоса под подписями оси X внутри SVG линейного графика: под ось зарезервировано
|
|
3387
|
+
* LINE_CHART_LABEL_MARGIN (20px), а подписи сдвинуты вниз на +16px, поэтому строка подписи
|
|
3388
|
+
* (шрифт d3 10px) кончается примерно в 14px от низа SVG.
|
|
3389
|
+
*/
|
|
3390
|
+
const LINE_CHART_X_LABELS_BLANK = 14;
|
|
3391
|
+
/** Зазор между подписями оси X и подписью самой оси — по макету. */
|
|
3392
|
+
const X_AXIS_TITLE_GAP = 4;
|
|
3393
|
+
/** Зазор между подписью оси X и легендой, идущей отдельной строкой, — по макету. */
|
|
3394
|
+
const X_AXIS_TITLE_LEGEND_GAP = 8;
|
|
3395
|
+
/** Выравнивание подписи оси X → `justify-content` строки-подвала графика. */
|
|
3396
|
+
const CHART_AXIS_ALIGN_JUSTIFY = {
|
|
3397
|
+
left: "flex-start",
|
|
3398
|
+
center: "center",
|
|
3399
|
+
right: "flex-end",
|
|
3400
|
+
};
|
|
3361
3401
|
const FILTER_PREFIX = "%";
|
|
3362
3402
|
/** Имя системного геометрического фильтра — выделение, нарисованное пользователем на карте. */
|
|
3363
3403
|
const GEOMETRY_FILTER_NAME = "geometry";
|
|
@@ -3467,7 +3507,76 @@ const CONTAINER_BODY_FILL_STYLE = {
|
|
|
3467
3507
|
*/
|
|
3468
3508
|
const CONTAINERS_GROUP_DEFAULTS = { boxSizing: "border-box" };
|
|
3469
3509
|
|
|
3470
|
-
|
|
3510
|
+
/** Обёртка линейного графика: строка горизонтальных подписей над телом, вертикальные — по бокам. */
|
|
3511
|
+
const LineChartLayout = styled.div.withConfig({ displayName: "LineChartLayout", componentId: "sc-gmzloe" }) `
|
|
3512
|
+
display: flex;
|
|
3513
|
+
flex-direction: column;
|
|
3514
|
+
width: 100%;
|
|
3515
|
+
height: 100%;
|
|
3516
|
+
min-width: 0;
|
|
3517
|
+
min-height: 0;
|
|
3518
|
+
`;
|
|
3519
|
+
const LineChartRow = styled.div.withConfig({ displayName: "LineChartRow", componentId: "sc-dxzi1m" }) `
|
|
3520
|
+
display: flex;
|
|
3521
|
+
flex: 1 1 auto;
|
|
3522
|
+
align-items: stretch;
|
|
3523
|
+
min-width: 0;
|
|
3524
|
+
min-height: 0;
|
|
3525
|
+
`;
|
|
3526
|
+
/**
|
|
3527
|
+
* Тело линейного графика между вертикальными подписями осей. Забирает всю оставшуюся ширину строки —
|
|
3528
|
+
* её и измеряем, чтобы отдать d3 пиксельную ширину: подписи бывают в одну и в две строки.
|
|
3529
|
+
*/
|
|
3530
|
+
const LineChartBody = styled.div.withConfig({ displayName: "LineChartBody", componentId: "sc-1sbsc7u" }) `
|
|
3531
|
+
flex: 1 1 auto;
|
|
3532
|
+
min-width: 0;
|
|
3533
|
+
`;
|
|
3534
|
+
const AxisTitlesRow = styled.div.withConfig({ displayName: "AxisTitlesRow", componentId: "sc-1hskx6f" }) `
|
|
3535
|
+
display: flex;
|
|
3536
|
+
flex: 0 0 ${AXIS_TITLE_TOP_HEIGHT}px;
|
|
3537
|
+
justify-content: space-between;
|
|
3538
|
+
align-items: flex-start;
|
|
3539
|
+
gap: 0.5rem;
|
|
3540
|
+
height: ${AXIS_TITLE_TOP_HEIGHT}px;
|
|
3541
|
+
min-width: 0;
|
|
3542
|
+
`;
|
|
3543
|
+
const AxisTitleText = styled.div.withConfig({ displayName: "AxisTitleText", componentId: "sc-9shs4l" }) `
|
|
3544
|
+
font-size: 0.625rem;
|
|
3545
|
+
line-height: ${AXIS_TITLE_LINE_HEIGHT}px;
|
|
3546
|
+
color: ${({ $color, theme: { palette } }) => $color || palette.iconDisabled};
|
|
3547
|
+
`;
|
|
3548
|
+
const AxisTitleTop = styled(AxisTitleText).withConfig({ displayName: "AxisTitleTop", componentId: "sc-pv3dgm" }) `
|
|
3549
|
+
overflow: hidden;
|
|
3550
|
+
white-space: nowrap;
|
|
3551
|
+
text-overflow: ellipsis;
|
|
3552
|
+
`;
|
|
3553
|
+
/**
|
|
3554
|
+
* Колонка вертикальной подписи оси: ширина — по тексту (одна или две строки) плюс зазор до значений
|
|
3555
|
+
* шкалы. Вертикальные отступы колонки повторяют поля SVG, поэтому подпись центрируется по полю
|
|
3556
|
+
* графика, а не по всей высоте вместе с подписями оси X.
|
|
3557
|
+
*/
|
|
3558
|
+
const AxisTitleSideColumn = styled.div.withConfig({ displayName: "AxisTitleSideColumn", componentId: "sc-11nmkfu" }) `
|
|
3559
|
+
display: flex;
|
|
3560
|
+
flex: 0 0 auto;
|
|
3561
|
+
align-items: center;
|
|
3562
|
+
justify-content: center;
|
|
3563
|
+
box-sizing: border-box;
|
|
3564
|
+
padding: ${({ $plotTop }) => $plotTop}px 0 ${({ $plotBottom }) => $plotBottom}px;
|
|
3565
|
+
${({ $side }) => ($side === "left" ? "padding-right" : "padding-left")}: ${AXIS_TITLE_GAP}px;
|
|
3566
|
+
`;
|
|
3567
|
+
/**
|
|
3568
|
+
* Сам текст вертикальной подписи. Поворот вынесен на внутренний элемент: повёрнутая целиком колонка
|
|
3569
|
+
* перенесла бы зазор до шкалы на внешнюю сторону. Левая подпись читается снизу вверх, правая — сверху
|
|
3570
|
+
* вниз, как на макете.
|
|
3571
|
+
*/
|
|
3572
|
+
const AxisTitleSide = styled(AxisTitleText).withConfig({ displayName: "AxisTitleSide", componentId: "sc-lretfl" }) `
|
|
3573
|
+
max-height: 100%;
|
|
3574
|
+
overflow: hidden;
|
|
3575
|
+
text-align: center;
|
|
3576
|
+
writing-mode: vertical-rl;
|
|
3577
|
+
transform: ${({ $side }) => ($side === "left" ? "rotate(180deg)" : "none")};
|
|
3578
|
+
`;
|
|
3579
|
+
const ChartTooltipTable = styled.table.withConfig({ displayName: "ChartTooltipTable", componentId: "sc-1j0a1z" }) `
|
|
3471
3580
|
td {
|
|
3472
3581
|
padding: 0;
|
|
3473
3582
|
}
|
|
@@ -3476,7 +3585,7 @@ const ChartTooltipTable = styled.table.withConfig({ displayName: "ChartTooltipTa
|
|
|
3476
3585
|
padding-bottom: 0.375rem;
|
|
3477
3586
|
}
|
|
3478
3587
|
`;
|
|
3479
|
-
const ChartTooltip = styled(uilibGl.Flex).withConfig({ displayName: "ChartTooltip", componentId: "sc-
|
|
3588
|
+
const ChartTooltip = styled(uilibGl.Flex).withConfig({ displayName: "ChartTooltip", componentId: "sc-1ps09qa" }) `
|
|
3480
3589
|
flex-wrap: nowrap;
|
|
3481
3590
|
background: rgb(48, 69, 79);
|
|
3482
3591
|
border-radius: 0.25rem;
|
|
@@ -3486,26 +3595,26 @@ const ChartTooltip = styled(uilibGl.Flex).withConfig({ displayName: "ChartToolti
|
|
|
3486
3595
|
margin-bottom: 0.25rem;
|
|
3487
3596
|
}
|
|
3488
3597
|
`;
|
|
3489
|
-
const ChartTooltipLabel = styled.div.withConfig({ displayName: "ChartTooltipLabel", componentId: "sc-
|
|
3598
|
+
const ChartTooltipLabel = styled.div.withConfig({ displayName: "ChartTooltipLabel", componentId: "sc-1chgokj" }) `
|
|
3490
3599
|
margin-right: 0.25rem;
|
|
3491
3600
|
`;
|
|
3492
|
-
const ChartTooltipColor = styled.div.withConfig({ displayName: "ChartTooltipColor", componentId: "sc-
|
|
3601
|
+
const ChartTooltipColor = styled.div.withConfig({ displayName: "ChartTooltipColor", componentId: "sc-1un94mw" }) `
|
|
3493
3602
|
width: 0.625rem;
|
|
3494
3603
|
height: 0.625rem;
|
|
3495
3604
|
background-color: ${({ $color }) => $color};
|
|
3496
3605
|
border-radius: ${({ theme: { borderRadius } }) => borderRadius.smallest};
|
|
3497
3606
|
margin-right: 0.25rem;
|
|
3498
3607
|
`;
|
|
3499
|
-
const ChartLegendColor$1 = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-
|
|
3608
|
+
const ChartLegendColor$1 = styled.div.withConfig({ displayName: "ChartLegendColor", componentId: "sc-sw1cfw" }) `
|
|
3500
3609
|
width: 0.5rem;
|
|
3501
3610
|
height: 0.5rem;
|
|
3502
3611
|
background-color: ${({ $color }) => $color};
|
|
3503
3612
|
border-radius: 0.125rem;
|
|
3504
3613
|
`;
|
|
3505
|
-
const ChartLegendValue = styled.div.withConfig({ displayName: "ChartLegendValue", componentId: "sc-
|
|
3614
|
+
const ChartLegendValue = styled.div.withConfig({ displayName: "ChartLegendValue", componentId: "sc-1s1kjum" }) `
|
|
3506
3615
|
opacity: 0.65;
|
|
3507
3616
|
`;
|
|
3508
|
-
const ChartTooltipRow = styled(uilibGl.Flex).withConfig({ displayName: "ChartTooltipRow", componentId: "sc-
|
|
3617
|
+
const ChartTooltipRow = styled(uilibGl.Flex).withConfig({ displayName: "ChartTooltipRow", componentId: "sc-ftllia" }) `
|
|
3509
3618
|
display: flex;
|
|
3510
3619
|
flex-direction: row;
|
|
3511
3620
|
flex-wrap: nowrap;
|
|
@@ -3517,11 +3626,11 @@ const ChartTooltipRow = styled(uilibGl.Flex).withConfig({ displayName: "ChartToo
|
|
|
3517
3626
|
margin-right: 0.25rem;
|
|
3518
3627
|
}
|
|
3519
3628
|
`;
|
|
3520
|
-
const ChartTooltipName = styled.div.withConfig({ displayName: "ChartTooltipName", componentId: "sc-
|
|
3629
|
+
const ChartTooltipName = styled.div.withConfig({ displayName: "ChartTooltipName", componentId: "sc-16ov53z" }) `
|
|
3521
3630
|
margin-right: 1rem;
|
|
3522
3631
|
opacity: 0.65;
|
|
3523
3632
|
`;
|
|
3524
|
-
const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", componentId: "sc-
|
|
3633
|
+
const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", componentId: "sc-5m536c" }) `
|
|
3525
3634
|
position: absolute;
|
|
3526
3635
|
top: 50%;
|
|
3527
3636
|
left: 50%;
|
|
@@ -3530,7 +3639,7 @@ const PieChartCenter = styled.div.withConfig({ displayName: "PieChartCenter", co
|
|
|
3530
3639
|
font-weight: bold;
|
|
3531
3640
|
font-size: 1.5rem;
|
|
3532
3641
|
`;
|
|
3533
|
-
const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapperContainer", componentId: "sc-
|
|
3642
|
+
const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapperContainer", componentId: "sc-1ixngl" }) `
|
|
3534
3643
|
position: relative;
|
|
3535
3644
|
width: 100%;
|
|
3536
3645
|
`;
|
|
@@ -3539,7 +3648,7 @@ const ChartWrapperContainer = styled.div.withConfig({ displayName: "ChartWrapper
|
|
|
3539
3648
|
* на неё вешается ref для ResizeObserver — меряем обе оси без цикла. Плоский div (не Flex uilib), как соседний
|
|
3540
3649
|
* ChartWrapperContainer, чтобы ref гарантированно дошёл до DOM-узла. Тело графика центрируется внутри.
|
|
3541
3650
|
*/
|
|
3542
|
-
const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure", componentId: "sc-
|
|
3651
|
+
const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure", componentId: "sc-3rqqmt" }) `
|
|
3543
3652
|
position: relative;
|
|
3544
3653
|
display: flex;
|
|
3545
3654
|
align-items: center;
|
|
@@ -3557,7 +3666,7 @@ const ChartFillMeasure = styled.div.withConfig({ displayName: "ChartFillMeasure"
|
|
|
3557
3666
|
display: block;
|
|
3558
3667
|
}
|
|
3559
3668
|
`;
|
|
3560
|
-
const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-
|
|
3669
|
+
const Tooltip = styled.div.withConfig({ displayName: "Tooltip", componentId: "sc-k91v0" }) `
|
|
3561
3670
|
position: relative;
|
|
3562
3671
|
border-radius: 0.25rem;
|
|
3563
3672
|
background-color: rgba(28, 33, 48);
|
|
@@ -3662,7 +3771,7 @@ const LineChartStyles = styled.createGlobalStyle `
|
|
|
3662
3771
|
fill: ${({ theme: { palette } }) => palette.textDisabled};
|
|
3663
3772
|
}
|
|
3664
3773
|
`;
|
|
3665
|
-
const StyledBarChart = styled(charts.BarChart).withConfig({ displayName: "StyledBarChart", componentId: "sc-
|
|
3774
|
+
const StyledBarChart = styled(charts.BarChart).withConfig({ displayName: "StyledBarChart", componentId: "sc-16gz38d" }) `
|
|
3666
3775
|
.domain {
|
|
3667
3776
|
display: none;
|
|
3668
3777
|
}
|
|
@@ -4550,6 +4659,79 @@ const getAttributeConfigurationByName = (attributes, name) => {
|
|
|
4550
4659
|
return attributes[name];
|
|
4551
4660
|
};
|
|
4552
4661
|
|
|
4662
|
+
const EMPTY_AXIS = {};
|
|
4663
|
+
const warnedLegacySources = new Set();
|
|
4664
|
+
// Сборщик консьюмера подставляет `process.env.NODE_ENV` строкой, поэтому выражение должно быть
|
|
4665
|
+
// ровно таким — без `typeof process` и без `?.`. Сам `process` webpack 5 в браузер не завозит:
|
|
4666
|
+
// проверка `typeof process` отключила бы предупреждения и в dev. Консьюмер без сборщика получит
|
|
4667
|
+
// ReferenceError — он гасится, и предупреждений просто нет.
|
|
4668
|
+
const isDevEnvironment = (() => {
|
|
4669
|
+
try {
|
|
4670
|
+
return process.env.NODE_ENV !== "production";
|
|
4671
|
+
}
|
|
4672
|
+
catch {
|
|
4673
|
+
return false;
|
|
4674
|
+
}
|
|
4675
|
+
})();
|
|
4676
|
+
/**
|
|
4677
|
+
* Один раз на источник сообщает, что конфиг остался на устаревшей плоской форме.
|
|
4678
|
+
*
|
|
4679
|
+
* Без такого маркера момент «легаси закончилось» не наступает никогда, и временный слой
|
|
4680
|
+
* совместимости становится постоянным.
|
|
4681
|
+
*/
|
|
4682
|
+
const warnLegacyAxis = ({ dataSourceName, attributeName }) => {
|
|
4683
|
+
if (!isDevEnvironment)
|
|
4684
|
+
return;
|
|
4685
|
+
const key = `${dataSourceName ?? "?"}:${attributeName ?? "?"}`;
|
|
4686
|
+
if (warnedLegacySources.has(key))
|
|
4687
|
+
return;
|
|
4688
|
+
warnedLegacySources.add(key);
|
|
4689
|
+
// eslint-disable-next-line no-console
|
|
4690
|
+
console.warn(`[Dashboard] Источник "${key}" использует устаревшие поля оси (chartAxis/axisColor/hideAxis). ` +
|
|
4691
|
+
"Перепишите их в объект axis: { type, side, color, title, hide }. " +
|
|
4692
|
+
"Слой совместимости временный.");
|
|
4693
|
+
};
|
|
4694
|
+
/**
|
|
4695
|
+
* Настройки оси источника.
|
|
4696
|
+
*
|
|
4697
|
+
* Читает `axis`, а при его отсутствии собирает объект из устаревших плоских полей
|
|
4698
|
+
* `chartAxis` / `axisColor` / `hideAxis`. Заданный `axis` имеет приоритет: узел, который уже
|
|
4699
|
+
* правили в редакторе, не должен откатываться к старым значениям.
|
|
4700
|
+
*/
|
|
4701
|
+
const resolveAxis = (source) => {
|
|
4702
|
+
if (!source)
|
|
4703
|
+
return EMPTY_AXIS;
|
|
4704
|
+
const { axis, chartAxis, axisColor, hideAxis } = source;
|
|
4705
|
+
if (axis)
|
|
4706
|
+
return axis;
|
|
4707
|
+
if (chartAxis === undefined && axisColor === undefined && hideAxis === undefined)
|
|
4708
|
+
return EMPTY_AXIS;
|
|
4709
|
+
warnLegacyAxis(source);
|
|
4710
|
+
return {
|
|
4711
|
+
type: chartAxis === "x" ? "x" : "y",
|
|
4712
|
+
color: axisColor,
|
|
4713
|
+
hide: hideAxis,
|
|
4714
|
+
};
|
|
4715
|
+
};
|
|
4716
|
+
/**
|
|
4717
|
+
* Источник питает ось значений.
|
|
4718
|
+
*
|
|
4719
|
+
* Отсутствие `axis.type` означает `"y"`: конфиг без явного типа — самый частый случай,
|
|
4720
|
+
* и раньше такой источник молча выпадал из графика.
|
|
4721
|
+
*/
|
|
4722
|
+
const isValueAxis = (source) => (resolveAxis(source).type ?? "y") === "y";
|
|
4723
|
+
/** Сторона оси; отсутствие `axis.side` означает `"left"`. */
|
|
4724
|
+
const getAxisSide = (source) => resolveAxis(source).side ?? "left";
|
|
4725
|
+
/** Источник выводится на правой оси. */
|
|
4726
|
+
const isRightAxis = (source) => getAxisSide(source) === "right";
|
|
4727
|
+
/**
|
|
4728
|
+
* Запись описывает ось чарта, а не источник фильтра.
|
|
4729
|
+
*
|
|
4730
|
+
* `BarChartFilter` собирает `relatedConfig` вручную и осей не задаёт — ему нужна исходная палитра,
|
|
4731
|
+
* а не растянутая под число элементов.
|
|
4732
|
+
*/
|
|
4733
|
+
const hasAxisConfig = ({ axis, chartAxis }) => axis !== undefined || chartAxis !== undefined;
|
|
4734
|
+
|
|
4553
4735
|
const getAttributesConfiguration = (layer) => {
|
|
4554
4736
|
const layerAttributeConfiguration = layer?.configuration?.attributesConfiguration ?? {};
|
|
4555
4737
|
const { geometryAttribute, idAttribute } = layerAttributeConfiguration;
|
|
@@ -4574,7 +4756,7 @@ const getAttributesConfiguration = (layer) => {
|
|
|
4574
4756
|
|
|
4575
4757
|
const formatChartRelatedValue = (t, value, layerInfo, relatedAttributes) => {
|
|
4576
4758
|
const layerDefinition = getAttributesConfiguration(layerInfo);
|
|
4577
|
-
const relatedAxis = relatedAttributes.find(
|
|
4759
|
+
const relatedAxis = relatedAttributes.find(isValueAxis);
|
|
4578
4760
|
const attribute = getAttributeConfigurationByName(layerDefinition.attributes, relatedAxis?.attributeName);
|
|
4579
4761
|
return attribute
|
|
4580
4762
|
? formatAttributeValue({ t, type: attribute.type, value, stringFormat: attribute.stringFormat })
|
|
@@ -5419,11 +5601,11 @@ const getAttributeValue = (element, attributes) => {
|
|
|
5419
5601
|
return typeof value === "string" && maxLength && maxLength < value.length ? (jsxRuntime.jsx(TextTrim, { maxLength: maxLength, wordBreak: wordBreak, expandable: expandable, lineBreak: lineBreak, children: value })) : (value);
|
|
5420
5602
|
};
|
|
5421
5603
|
|
|
5422
|
-
const getChartAxes = (chartElement) => chartElement?.options?.relatedDataSources?.filter(
|
|
5604
|
+
const getChartAxes = (chartElement) => chartElement?.options?.relatedDataSources?.filter(isValueAxis);
|
|
5423
5605
|
|
|
5424
5606
|
const getChartFilterName = (relatedDataSources) => {
|
|
5425
5607
|
const relatedAttributes = relatedDataSources || [];
|
|
5426
|
-
const axes = relatedAttributes.filter(
|
|
5608
|
+
const axes = relatedAttributes.filter(isValueAxis);
|
|
5427
5609
|
return axes?.[0]?.filterName;
|
|
5428
5610
|
};
|
|
5429
5611
|
|
|
@@ -7053,6 +7235,96 @@ const useFetchImageWithAuth = (url) => {
|
|
|
7053
7235
|
return isExternal ? url : blobUrl;
|
|
7054
7236
|
};
|
|
7055
7237
|
|
|
7238
|
+
/**
|
|
7239
|
+
* Оси значений линейного графика и атрибуты, по `stringFormat` которых форматируются деления.
|
|
7240
|
+
*
|
|
7241
|
+
* У левой и правой осей это разные атрибуты, поэтому они резолвятся по отдельности: иначе на
|
|
7242
|
+
* правой оси окажутся единицы измерения левой.
|
|
7243
|
+
*/
|
|
7244
|
+
const useChartAxisAttributes = ({ dataSources, relatedAttributes }) => {
|
|
7245
|
+
const { layerInfos, dataSources: fetchedDataSources } = useWidgetContext();
|
|
7246
|
+
const axes = React.useMemo(() => (relatedAttributes || []).filter(isValueAxis), [relatedAttributes]);
|
|
7247
|
+
const strokeColors = React.useMemo(() => axes.map(related => resolveAxis(related).color), [axes]);
|
|
7248
|
+
const leftAxis = React.useMemo(() => axes.find(related => !isRightAxis(related)) ?? axes[0] ?? relatedAttributes?.[0], [axes, relatedAttributes]);
|
|
7249
|
+
const rightAxis = React.useMemo(() => axes.find(isRightAxis), [axes]);
|
|
7250
|
+
const resolveAxisAttribute = React.useCallback((related) => {
|
|
7251
|
+
if (!related)
|
|
7252
|
+
return undefined;
|
|
7253
|
+
const layerInfo = getDataSourceLayerInfo({
|
|
7254
|
+
layerInfos,
|
|
7255
|
+
configDataSource: dataSources?.find(({ name }) => name === related.dataSourceName),
|
|
7256
|
+
fetchedDataSource: getDataSource(related.dataSourceName, fetchedDataSources),
|
|
7257
|
+
});
|
|
7258
|
+
return getLayerInfoAttribute(layerInfo, related.attributeName);
|
|
7259
|
+
}, [dataSources, fetchedDataSources, layerInfos]);
|
|
7260
|
+
const leftAxisAttribute = React.useMemo(() => resolveAxisAttribute(leftAxis), [resolveAxisAttribute, leftAxis]);
|
|
7261
|
+
const rightAxisAttribute = React.useMemo(() => resolveAxisAttribute(rightAxis), [resolveAxisAttribute, rightAxis]);
|
|
7262
|
+
return { axes, strokeColors, leftAxisAttribute, rightAxisAttribute };
|
|
7263
|
+
};
|
|
7264
|
+
|
|
7265
|
+
/**
|
|
7266
|
+
* Форматтеры делений левой и правой шкал линейного графика — по `stringFormat` своего атрибута.
|
|
7267
|
+
*
|
|
7268
|
+
* Отдаются в `tickFormat` генератора оси, а не подменяют текст после отрисовки: d3 измеряет ширину
|
|
7269
|
+
* шкалы по уже нарисованным подписям и по ней сдвигает поле графика. Подмена текста задним числом
|
|
7270
|
+
* (`30` → `30,0`) делала подпись шире замера, и она заезжала на подпись оси слева.
|
|
7271
|
+
*/
|
|
7272
|
+
const useChartAxisTickFormat = ({ dataSources, relatedAttributes }) => {
|
|
7273
|
+
const { t } = useGlobalContext();
|
|
7274
|
+
const { leftAxisAttribute, rightAxisAttribute } = useChartAxisAttributes({ dataSources, relatedAttributes });
|
|
7275
|
+
const createTickFormat = React.useCallback((attribute) => (value) => {
|
|
7276
|
+
if (!value)
|
|
7277
|
+
return "0";
|
|
7278
|
+
return attribute
|
|
7279
|
+
? String(formatAttributeValue({
|
|
7280
|
+
t,
|
|
7281
|
+
type: attribute.type,
|
|
7282
|
+
value: Number(value),
|
|
7283
|
+
stringFormat: attribute.stringFormat,
|
|
7284
|
+
noUnits: true,
|
|
7285
|
+
}))
|
|
7286
|
+
: String(value);
|
|
7287
|
+
}, [t]);
|
|
7288
|
+
const formatLeftTick = React.useMemo(() => createTickFormat(leftAxisAttribute), [createTickFormat, leftAxisAttribute]);
|
|
7289
|
+
const formatRightTick = React.useMemo(() => createTickFormat(rightAxisAttribute), [createTickFormat, rightAxisAttribute]);
|
|
7290
|
+
return { formatLeftTick, formatRightTick };
|
|
7291
|
+
};
|
|
7292
|
+
|
|
7293
|
+
/**
|
|
7294
|
+
* Подписи осей линейного графика и место, которое они занимают над телом графика.
|
|
7295
|
+
*
|
|
7296
|
+
* Режим размещения общий для всех осей (`options.axis.titlePosition`), текст — у каждой оси свой.
|
|
7297
|
+
* Цвет подписи отдельной опцией не задаётся: одна серия на стороне — подпись в её цвет, несколько —
|
|
7298
|
+
* нейтральная, потому что тогда подпись описывает всю ось, а не одну из линий. Ширину вертикальных
|
|
7299
|
+
* подписей здесь не считаем — она зависит от числа строк текста и измеряется по факту.
|
|
7300
|
+
*/
|
|
7301
|
+
const useChartAxisTitles = ({ options, axes, isLineChart }) => {
|
|
7302
|
+
const axisTitlePosition = options?.axis?.titlePosition ?? "side";
|
|
7303
|
+
const axisTitles = React.useMemo(() => {
|
|
7304
|
+
const pick = (side) => {
|
|
7305
|
+
const sideAxes = axes.filter(related => getAxisSide(related) === side);
|
|
7306
|
+
const title = sideAxes.map(resolveAxis).find(axis => axis.title)?.title;
|
|
7307
|
+
if (!title)
|
|
7308
|
+
return undefined;
|
|
7309
|
+
return { title, color: sideAxes.length === 1 ? resolveAxis(sideAxes[0]).color : undefined };
|
|
7310
|
+
};
|
|
7311
|
+
return { left: pick("left"), right: pick("right") };
|
|
7312
|
+
}, [axes]);
|
|
7313
|
+
const hasAxisTitles = !!(isLineChart && (axisTitles.left || axisTitles.right));
|
|
7314
|
+
const hasTopTitles = hasAxisTitles && axisTitlePosition === "top";
|
|
7315
|
+
const hasSideTitles = hasAxisTitles && axisTitlePosition === "side";
|
|
7316
|
+
const hideLeftAxis = React.useMemo(() => axes.filter(related => !isRightAxis(related)).every(related => resolveAxis(related).hide), [axes]);
|
|
7317
|
+
const hideRightAxis = React.useMemo(() => axes.filter(isRightAxis).every(related => resolveAxis(related).hide), [axes]);
|
|
7318
|
+
return {
|
|
7319
|
+
axisTitles,
|
|
7320
|
+
hasTopTitles,
|
|
7321
|
+
hasSideTitles,
|
|
7322
|
+
axisTitlesHeight: hasTopTitles ? AXIS_TITLE_TOP_HEIGHT : 0,
|
|
7323
|
+
hideLeftAxis,
|
|
7324
|
+
hideRightAxis,
|
|
7325
|
+
};
|
|
7326
|
+
};
|
|
7327
|
+
|
|
7056
7328
|
const HEIGHT_OFFSET = 20;
|
|
7057
7329
|
const FILL_OPACITY = 0.28;
|
|
7058
7330
|
const lineGenerator = line()
|
|
@@ -7098,20 +7370,10 @@ function wrap() {
|
|
|
7098
7370
|
}
|
|
7099
7371
|
}
|
|
7100
7372
|
const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes, defaultColor, fontColor, markers, showMarkers, }) => {
|
|
7101
|
-
const {
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
.map(({ axisColor }) => axisColor);
|
|
7106
|
-
const yAxis = React.useMemo(() => relatedAttributes?.find(({ chartAxis }) => chartAxis === "y") ?? relatedAttributes?.[0], [relatedAttributes]);
|
|
7107
|
-
const yAxisAttribute = React.useMemo(() => {
|
|
7108
|
-
const layerInfo = getDataSourceLayerInfo({
|
|
7109
|
-
layerInfos,
|
|
7110
|
-
configDataSource: dataSources?.find(({ name }) => name === yAxis?.dataSourceName),
|
|
7111
|
-
fetchedDataSource: getDataSource(yAxis?.dataSourceName, fetchedDataSources),
|
|
7112
|
-
});
|
|
7113
|
-
return getLayerInfoAttribute(layerInfo, yAxis?.attributeName);
|
|
7114
|
-
}, [dataSources, fetchedDataSources, layerInfos, yAxis]);
|
|
7373
|
+
const { strokeColors } = useChartAxisAttributes({
|
|
7374
|
+
dataSources,
|
|
7375
|
+
relatedAttributes,
|
|
7376
|
+
});
|
|
7115
7377
|
const ref = React.useRef({
|
|
7116
7378
|
path: null,
|
|
7117
7379
|
area: null,
|
|
@@ -7164,22 +7426,9 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
|
|
|
7164
7426
|
svg
|
|
7165
7427
|
.selectAll(`.${charts.lineChartClassNames.lineChartXScaleGlobal} .tick text`)
|
|
7166
7428
|
.attr("y", 16);
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
if (!item)
|
|
7171
|
-
return 0;
|
|
7172
|
-
return yAxisAttribute
|
|
7173
|
-
? formatAttributeValue({
|
|
7174
|
-
t,
|
|
7175
|
-
type: yAxisAttribute.type,
|
|
7176
|
-
value: item,
|
|
7177
|
-
stringFormat: yAxisAttribute.stringFormat,
|
|
7178
|
-
noUnits: true,
|
|
7179
|
-
})
|
|
7180
|
-
: item;
|
|
7181
|
-
})
|
|
7182
|
-
.each(wrap);
|
|
7429
|
+
// Текст делений форматируется ещё в генераторе оси (useChartAxisTickFormat), до замера ширины
|
|
7430
|
+
// шкалы; здесь его только укорачиваем.
|
|
7431
|
+
svg.selectAll(`.${charts.lineChartClassNames.lineChartYScaleGlobal} .tick text`).each(wrap);
|
|
7183
7432
|
svg.selectAll(".tick text").attr("style", `color: ${fontColor}`);
|
|
7184
7433
|
const global = svg.select(`.${charts.lineChartClassNames.lineChartLinesGlobal}`);
|
|
7185
7434
|
const lineChartLines = svg.selectAll(`.${charts.lineChartClassNames.lineChartLine}`);
|
|
@@ -7215,7 +7464,6 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
|
|
|
7215
7464
|
svg.append("defs").html(() => defs.join(""));
|
|
7216
7465
|
}, [
|
|
7217
7466
|
fontColor,
|
|
7218
|
-
yAxisAttribute,
|
|
7219
7467
|
chartId,
|
|
7220
7468
|
strokeColors,
|
|
7221
7469
|
defaultColor,
|
|
@@ -7223,7 +7471,6 @@ const useChartChange = ({ dataSources, chartId, width, height, relatedAttributes
|
|
|
7223
7471
|
onChange,
|
|
7224
7472
|
showMarkers,
|
|
7225
7473
|
markers,
|
|
7226
|
-
t,
|
|
7227
7474
|
]);
|
|
7228
7475
|
return [customize, onChange];
|
|
7229
7476
|
};
|
|
@@ -7380,10 +7627,10 @@ const useChartData = ({ element, type }) => {
|
|
|
7380
7627
|
const configDataSources = useConfigDataSources(type);
|
|
7381
7628
|
const relatedAttributes = React.useMemo(() => element?.options?.relatedDataSources || [], [element?.options?.relatedDataSources]);
|
|
7382
7629
|
const loading = React.useMemo(() => !!relatedAttributes?.length &&
|
|
7383
|
-
!dataSources?.some(({ name }) => relatedAttributes.some(
|
|
7630
|
+
!dataSources?.some(({ name }) => relatedAttributes.some(related => isValueAxis(related) && related.dataSourceName === name)), [dataSources, relatedAttributes]);
|
|
7384
7631
|
const fetchedData = React.useMemo(() => {
|
|
7385
7632
|
const isRelated = !!relatedAttributes?.length;
|
|
7386
|
-
const filteredAttributes = relatedAttributes.filter(
|
|
7633
|
+
const filteredAttributes = relatedAttributes.filter(isValueAxis);
|
|
7387
7634
|
if (!element)
|
|
7388
7635
|
return [];
|
|
7389
7636
|
if (isRelated) {
|
|
@@ -7391,7 +7638,7 @@ const useChartData = ({ element, type }) => {
|
|
|
7391
7638
|
return [];
|
|
7392
7639
|
return filteredAttributes
|
|
7393
7640
|
.map(relatedAxis => {
|
|
7394
|
-
const { dataSourceName,
|
|
7641
|
+
const { dataSourceName, attributeName, attributeUnits } = relatedAxis;
|
|
7395
7642
|
const dataSource = configDataSources.find(({ name }) => name === dataSourceName);
|
|
7396
7643
|
const featureDataSource = getDataSource(dataSourceName, dataSources);
|
|
7397
7644
|
const layerInfo = getDataSourceLayerInfo({
|
|
@@ -7411,7 +7658,10 @@ const useChartData = ({ element, type }) => {
|
|
|
7411
7658
|
layerInfo,
|
|
7412
7659
|
})
|
|
7413
7660
|
: null,
|
|
7414
|
-
color:
|
|
7661
|
+
color: resolveAxis(relatedAxis).color,
|
|
7662
|
+
// Сторона едет вместе с данными: серии без значений отсеиваются ниже, и сопоставлять
|
|
7663
|
+
// их с осями по индексу уже нельзя.
|
|
7664
|
+
axisSide: getAxisSide(relatedAxis),
|
|
7415
7665
|
attributeName,
|
|
7416
7666
|
attributeUnits,
|
|
7417
7667
|
dataSourceName,
|
|
@@ -7424,6 +7674,7 @@ const useChartData = ({ element, type }) => {
|
|
|
7424
7674
|
return [
|
|
7425
7675
|
{
|
|
7426
7676
|
items: getDataFromAttributes(t, element, attributes),
|
|
7677
|
+
axisSide: "left",
|
|
7427
7678
|
layerInfo: null,
|
|
7428
7679
|
attributeName: null,
|
|
7429
7680
|
attributeUnits: null,
|
|
@@ -7443,7 +7694,7 @@ const useChartData = ({ element, type }) => {
|
|
|
7443
7694
|
]);
|
|
7444
7695
|
const isResolved = React.useMemo(() => !relatedAttributes?.length ||
|
|
7445
7696
|
relatedAttributes
|
|
7446
|
-
.filter(
|
|
7697
|
+
.filter(isValueAxis)
|
|
7447
7698
|
.every(({ dataSourceName }) => Array.isArray(getDataSource(dataSourceName, dataSources)?.features)), [relatedAttributes, dataSources]);
|
|
7448
7699
|
const [data, setData] = React.useState(fetchedData);
|
|
7449
7700
|
React.useEffect(() => {
|
|
@@ -8975,6 +9226,15 @@ const ChartLegendContainer = styled(uilibGl.Flex).withConfig({ displayName: "Cha
|
|
|
8975
9226
|
flex-direction: column;
|
|
8976
9227
|
flex-wrap: wrap;
|
|
8977
9228
|
justify-content: ${({ twoColumns }) => (twoColumns ? "flex-start" : "center")};
|
|
9229
|
+
|
|
9230
|
+
/* Явная опция column перебивает раскладку, которую легенде навязывает ChartContainer
|
|
9231
|
+
(под графиком — в ряд, сбоку — столбиком). Без опции поведение прежнее. */
|
|
9232
|
+
${({ $column }) => $column !== undefined &&
|
|
9233
|
+
styled.css `
|
|
9234
|
+
&&& {
|
|
9235
|
+
flex-direction: ${$column ? "column" : "row"};
|
|
9236
|
+
}
|
|
9237
|
+
`}
|
|
8978
9238
|
`;
|
|
8979
9239
|
const ChartLegendItem = styled(uilibGl.Flex).withConfig({ displayName: "ChartLegendItem", componentId: "sc-16m9piw" }) `
|
|
8980
9240
|
cursor: ${({ onClick }) => (onClick ? "pointer" : "default")};
|
|
@@ -9043,12 +9303,129 @@ const ContainerChart = styled(uilibGl.Flex).withConfig({ displayName: "Container
|
|
|
9043
9303
|
`}
|
|
9044
9304
|
`;
|
|
9045
9305
|
const ContainerLegend = styled(uilibGl.Flex).withConfig({ displayName: "ContainerLegend", componentId: "sc-z1n1s8" }) ``;
|
|
9046
|
-
|
|
9306
|
+
/**
|
|
9307
|
+
* Выравнивание строки по полю графика: ширина тела графика, а внутренние отступы — до краёв поля
|
|
9308
|
+
* (колонка подписи оси и значения шкалы). Тело графика центрируется в слоте, поэтому и строка
|
|
9309
|
+
* центрируется. Правило поднято `&&&` над шириной, которую строкам задаёт `ContainerValue`.
|
|
9310
|
+
*/
|
|
9311
|
+
const plotInsetsMixin = styled.css `
|
|
9312
|
+
${({ $insets }) => $insets &&
|
|
9313
|
+
styled.css `
|
|
9314
|
+
&&& {
|
|
9315
|
+
box-sizing: border-box;
|
|
9316
|
+
align-self: center;
|
|
9317
|
+
width: ${$insets.width}px;
|
|
9318
|
+
max-width: 100%;
|
|
9319
|
+
padding-left: ${$insets.left}px;
|
|
9320
|
+
padding-right: ${$insets.right}px;
|
|
9321
|
+
}
|
|
9322
|
+
`}
|
|
9323
|
+
`;
|
|
9324
|
+
/**
|
|
9325
|
+
* Строка ждёт, пока график сообщит положение поля: место занимает, но не видна. Иначе до первой
|
|
9326
|
+
* отрисовки d3 она стояла бы по краю контейнера и потом прыгала на место.
|
|
9327
|
+
*/
|
|
9328
|
+
const awaitingPlotMixin = styled.css `
|
|
9329
|
+
${({ $awaitingPlot }) => $awaitingPlot &&
|
|
9330
|
+
styled.css `
|
|
9331
|
+
visibility: hidden;
|
|
9332
|
+
`}
|
|
9333
|
+
`;
|
|
9334
|
+
/**
|
|
9335
|
+
* Строка под телом графика: подпись оси X и, при `legendInline`, легенда справа от неё.
|
|
9336
|
+
*
|
|
9337
|
+
* `$offsetTop` подтягивает строку к подписям оси X: под ними внутри SVG остаётся пустая полоса,
|
|
9338
|
+
* и без поправки зазор получился бы втрое больше макетного.
|
|
9339
|
+
*/
|
|
9340
|
+
const ChartFooter = styled(uilibGl.Flex).withConfig({ displayName: "ChartFooter", componentId: "sc-nii28w" }) `
|
|
9341
|
+
align-items: center;
|
|
9342
|
+
flex-wrap: nowrap;
|
|
9343
|
+
gap: 0.5rem;
|
|
9344
|
+
width: 100%;
|
|
9345
|
+
margin-top: ${({ $offsetTop }) => $offsetTop ?? 0}px;
|
|
9346
|
+
justify-content: ${({ $align }) => $align};
|
|
9347
|
+
|
|
9348
|
+
${ContainerLegend} {
|
|
9349
|
+
justify-content: flex-end;
|
|
9350
|
+
margin-left: auto;
|
|
9351
|
+
}
|
|
9352
|
+
|
|
9353
|
+
/* Легенда в строке с подписью: записи прижаты вправо, без отступа сверху, который ей задаёт
|
|
9354
|
+
ContainerValue, и без нижнего отступа записей — иначе она съедет вниз относительно подписи.
|
|
9355
|
+
Прижимать нужно сами записи: обёртки легенды — Flex из uilib-gl с width: 100%, и выравнивание
|
|
9356
|
+
внешнего контейнера до записей не доходит, а своё у легенды — по центру. */
|
|
9357
|
+
&&& ${ChartLegendContainer} {
|
|
9358
|
+
justify-content: flex-end;
|
|
9359
|
+
margin-top: 0;
|
|
9360
|
+
}
|
|
9361
|
+
|
|
9362
|
+
&&& ${ChartLegendItem} {
|
|
9363
|
+
margin-bottom: 0;
|
|
9364
|
+
}
|
|
9365
|
+
|
|
9366
|
+
${plotInsetsMixin};
|
|
9367
|
+
${awaitingPlotMixin};
|
|
9368
|
+
`;
|
|
9369
|
+
const ChartXAxisTitle = styled.div.withConfig({ displayName: "ChartXAxisTitle", componentId: "sc-1vsgzi9" }) `
|
|
9370
|
+
font-size: 0.625rem;
|
|
9371
|
+
line-height: 0.75rem;
|
|
9372
|
+
white-space: nowrap;
|
|
9373
|
+
color: ${({ $color, theme: { palette } }) => $color || palette.textDisabled};
|
|
9374
|
+
`;
|
|
9375
|
+
/**
|
|
9376
|
+
* Строка легенды под графиком. `$gapTop` заменяет отступ сверху, который легенде задаёт `ContainerValue`;
|
|
9377
|
+
* `$offsetTop` сдвигает саму строку — подтягивает её к подписям оси X, как строку с подписью оси.
|
|
9378
|
+
*
|
|
9379
|
+
* `$justify` выравнивает сами записи. Выравнивание строки (`justifyContent`) до них не доходит: обёртки
|
|
9380
|
+
* легенды — Flex из uilib-gl с `width: 100%`, а записи внутри легенда всегда центрирует.
|
|
9381
|
+
*/
|
|
9382
|
+
const ChartLegendRow = styled(ContainerLegend).withConfig({ displayName: "ChartLegendRow", componentId: "sc-1a12wrz" }) `
|
|
9383
|
+
${({ $offsetTop }) => $offsetTop !== undefined &&
|
|
9384
|
+
styled.css `
|
|
9385
|
+
margin-top: ${$offsetTop}px;
|
|
9386
|
+
`}
|
|
9387
|
+
|
|
9388
|
+
${({ $gapTop }) => $gapTop !== undefined &&
|
|
9389
|
+
styled.css `
|
|
9390
|
+
&&& ${ChartLegendContainer} {
|
|
9391
|
+
margin-top: ${$gapTop}px;
|
|
9392
|
+
}
|
|
9393
|
+
`}
|
|
9394
|
+
|
|
9395
|
+
${({ $justify }) => $justify &&
|
|
9396
|
+
styled.css `
|
|
9397
|
+
&&& ${ChartLegendContainer} {
|
|
9398
|
+
justify-content: ${$justify};
|
|
9399
|
+
}
|
|
9400
|
+
`}
|
|
9401
|
+
|
|
9402
|
+
${plotInsetsMixin};
|
|
9403
|
+
${awaitingPlotMixin};
|
|
9404
|
+
`;
|
|
9405
|
+
/**
|
|
9406
|
+
* Колонка «график + подпись оси X» для легенды сбоку (`twoColumns`). Без неё подпись встала бы третьим
|
|
9407
|
+
* элементом ряда рядом с легендой, а не под графиком.
|
|
9408
|
+
*/
|
|
9409
|
+
const ChartWithFooter = styled(uilibGl.Flex).withConfig({ displayName: "ChartWithFooter", componentId: "sc-140gv2s" }) `
|
|
9410
|
+
flex-direction: column;
|
|
9411
|
+
flex-wrap: nowrap;
|
|
9412
|
+
min-width: 0;
|
|
9413
|
+
|
|
9414
|
+
${({ $fill }) => $fill &&
|
|
9415
|
+
styled.css `
|
|
9416
|
+
min-height: 0;
|
|
9417
|
+
`}
|
|
9418
|
+
|
|
9419
|
+
&&& ${ContainerChart} {
|
|
9420
|
+
width: 100%;
|
|
9421
|
+
}
|
|
9422
|
+
`;
|
|
9423
|
+
const ContainerUnits = styled.div.withConfig({ displayName: "ContainerUnits", componentId: "sc-vj15z6" }) `
|
|
9047
9424
|
margin-left: 0.5rem;
|
|
9048
9425
|
white-space: nowrap;
|
|
9049
9426
|
font-size: 0.75rem;
|
|
9050
9427
|
`;
|
|
9051
|
-
const ContainerValue = styled(uilibGl.Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-
|
|
9428
|
+
const ContainerValue = styled(uilibGl.Flex).withConfig({ displayName: "ContainerValue", componentId: "sc-1bnfj5v" }) `
|
|
9052
9429
|
justify-content: flex-end;
|
|
9053
9430
|
align-items: center;
|
|
9054
9431
|
flex-wrap: nowrap;
|
|
@@ -9067,7 +9444,7 @@ const ContainerValue = styled(uilibGl.Flex).withConfig({ displayName: "Container
|
|
|
9067
9444
|
width: ${({ column }) => (column ? "100%" : "auto")};
|
|
9068
9445
|
}
|
|
9069
9446
|
|
|
9070
|
-
${ContainerChart}, ${ContainerLegend} {
|
|
9447
|
+
${ContainerChart}, ${ContainerLegend}, ${ChartWithFooter} {
|
|
9071
9448
|
width: ${({ column }) => (column ? "100%" : "50%")};
|
|
9072
9449
|
}
|
|
9073
9450
|
|
|
@@ -9090,7 +9467,7 @@ const SizeIconMixin = styled.css `
|
|
|
9090
9467
|
font-size: ${({ $fontSize }) => $fontSize}px !important;
|
|
9091
9468
|
}
|
|
9092
9469
|
`;
|
|
9093
|
-
styled(uilibGl.Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-
|
|
9470
|
+
styled(uilibGl.Icon).withConfig({ displayName: "ContainerIcon", componentId: "sc-18399bz" }) `
|
|
9094
9471
|
width: auto;
|
|
9095
9472
|
height: auto;
|
|
9096
9473
|
margin-bottom: 0.5rem;
|
|
@@ -9104,7 +9481,7 @@ const SvgContainerColorMixin$1 = styled.css `
|
|
|
9104
9481
|
fill: ${({ $fontColor }) => $fontColor};
|
|
9105
9482
|
}
|
|
9106
9483
|
`;
|
|
9107
|
-
const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-
|
|
9484
|
+
const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", componentId: "sc-8l0ajh" }) `
|
|
9108
9485
|
&&& {
|
|
9109
9486
|
min-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
9110
9487
|
max-width: ${({ $width }) => ($width ? `${$width}px` : "1rem")};
|
|
@@ -9116,7 +9493,7 @@ const SvgContainer$1 = styled.div.withConfig({ displayName: "SvgContainer", comp
|
|
|
9116
9493
|
}
|
|
9117
9494
|
}
|
|
9118
9495
|
`;
|
|
9119
|
-
const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-
|
|
9496
|
+
const TwoColumnContainerWrapper = styled(Container).withConfig({ displayName: "TwoColumnContainerWrapper", componentId: "sc-1yxeknf" }) `
|
|
9120
9497
|
flex-direction: row;
|
|
9121
9498
|
flex-wrap: nowrap;
|
|
9122
9499
|
align-items: center;
|
|
@@ -9143,6 +9520,16 @@ const CameraContainer = React.memo(({ elementConfig, type, renderElement }) => {
|
|
|
9143
9520
|
return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { ...body, children: [jsxRuntime.jsx(ContainerAlias, { hasBottomMargin: true, children: renderElement({ id: "alias" }) }), jsxRuntime.jsx(ContainerValue, { children: renderElement({ id: "value", wrap: false }) })] }))] }));
|
|
9144
9521
|
});
|
|
9145
9522
|
|
|
9523
|
+
/**
|
|
9524
|
+
* Контекст, через который элемент `chart` сообщает контейнеру, где лежит поле графика.
|
|
9525
|
+
*
|
|
9526
|
+
* По макету подпись оси X и легенда выравниваются по полю графика, а не по краям контейнера.
|
|
9527
|
+
* Ширину шкал d3 вычисляет только при отрисовке, а подвал с подписью и легендой рисует
|
|
9528
|
+
* `ChartContainer` — снаружи элемента. Без контейнера (элемент сам по себе) провайдера нет,
|
|
9529
|
+
* и отчёт просто не отправляется.
|
|
9530
|
+
*/
|
|
9531
|
+
const ChartPlotContext = React.createContext({});
|
|
9532
|
+
|
|
9146
9533
|
/**
|
|
9147
9534
|
* Контекст вписывания графика (`options.fill` контейнера Chart).
|
|
9148
9535
|
*
|
|
@@ -9152,14 +9539,40 @@ const CameraContainer = React.memo(({ elementConfig, type, renderElement }) => {
|
|
|
9152
9539
|
*/
|
|
9153
9540
|
const FillContext = React.createContext({ fill: false, fitHeight: false });
|
|
9154
9541
|
|
|
9542
|
+
const isSameInsets = (prev, next) => !!prev && prev.left === next.left && prev.right === next.right && prev.width === next.width;
|
|
9155
9543
|
const ChartContainer = React.memo(({ elementConfig, isVisible, type, renderElement }) => {
|
|
9156
9544
|
const { options, children } = elementConfig || {};
|
|
9157
|
-
const { twoColumns, hideEmpty, height } = options || {};
|
|
9545
|
+
const { twoColumns, hideEmpty, height, legendInline } = options || {};
|
|
9158
9546
|
const fill = options?.fill;
|
|
9159
9547
|
const fillContextValue = React.useMemo(() => ({ fill: !!fill, fitHeight: !!fill && height != null }), [fill, height]);
|
|
9548
|
+
// Поле графика отчитывается после каждой отрисовки d3; одинаковые значения не пишем в стейт,
|
|
9549
|
+
// иначе отчёт и ре-рендер гоняли бы друг друга.
|
|
9550
|
+
const [plotInsets, setPlotInsets] = React.useState(null);
|
|
9551
|
+
const reportPlotInsets = React.useCallback((next) => setPlotInsets(prev => (isSameInsets(prev, next) ? prev : next)), []);
|
|
9552
|
+
const plotContextValue = React.useMemo(() => ({ reportPlotInsets }), [reportPlotInsets]);
|
|
9160
9553
|
const aliasElement = children.find(child => child.id === "alias");
|
|
9161
9554
|
const chartElement = children.find(child => child.id === "chart");
|
|
9162
9555
|
const legendElement = children.find(child => child.id === "legend");
|
|
9556
|
+
const isLineChart = chartElement?.options?.chartType === "line";
|
|
9557
|
+
const xAxisTitle = chartElement?.options?.axis?.x?.title;
|
|
9558
|
+
const footerJustify = React.useMemo(() => CHART_AXIS_ALIGN_JUSTIFY[chartElement?.options?.axis?.x?.align ?? "center"], [chartElement?.options?.axis?.x?.align]);
|
|
9559
|
+
// Легенда встаёт в строку с подписью оси X только когда та есть и легенда не вынесена вбок.
|
|
9560
|
+
const isLegendInline = React.useMemo(() => !!legendInline && !twoColumns && !!xAxisTitle, [legendInline, twoColumns, xAxisTitle]);
|
|
9561
|
+
// По макету подпись оси X и легенда у линейного графика выравниваются по полю графика, а не по
|
|
9562
|
+
// контейнеру. Легенда сбоку (twoColumns) живёт в своей колонке и в выравнивании не участвует.
|
|
9563
|
+
const footerInsets = React.useMemo(() => (isLineChart ? plotInsets : null), [isLineChart, plotInsets]);
|
|
9564
|
+
const isPlotLegend = isLineChart && !twoColumns;
|
|
9565
|
+
// Положение поля становится известно только после отрисовки: LineChart рисует d3 в useEffect, уже
|
|
9566
|
+
// после первого кадра. До отчёта подвал встал бы по краю контейнера и затем прыгнул на место, поэтому
|
|
9567
|
+
// прячем его — место под ним сохраняется, а появляется он вместе с линиями графика.
|
|
9568
|
+
const isAwaitingPlot = isLineChart && !plotInsets;
|
|
9569
|
+
// Пока подпись оси X не задана, легенда выравнивается прежней опцией `center` (по умолчанию — к левой
|
|
9570
|
+
// границе поля графика); с подписью — по ней, иначе подпись и легенда разъезжаются по разным краям.
|
|
9571
|
+
const legendJustify = React.useMemo(() => {
|
|
9572
|
+
if (xAxisTitle)
|
|
9573
|
+
return footerJustify;
|
|
9574
|
+
return legendElement?.options?.center ? "center" : "flex-start";
|
|
9575
|
+
}, [xAxisTitle, footerJustify, legendElement?.options?.center]);
|
|
9163
9576
|
const { root, body } = useContainerRoot({ elementConfig });
|
|
9164
9577
|
const { data, loading } = useChartData({
|
|
9165
9578
|
element: chartElement,
|
|
@@ -9171,7 +9584,12 @@ const ChartContainer = React.memo(({ elementConfig, isVisible, type, renderEleme
|
|
|
9171
9584
|
const hasItems = !!data[0]?.items?.length;
|
|
9172
9585
|
if (!loading && !hasItems && hideEmpty)
|
|
9173
9586
|
return null;
|
|
9174
|
-
|
|
9587
|
+
const chartSlot = (jsxRuntime.jsx(ContainerChart, { "$fill": !!fill, children: jsxRuntime.jsx(FillContext.Provider, { value: fillContextValue, children: jsxRuntime.jsx(ChartPlotContext.Provider, { value: plotContextValue, children: renderElement({ id: "chart" }) }) }) }));
|
|
9588
|
+
const footer = xAxisTitle ? (jsxRuntime.jsxs(ChartFooter, { "$align": footerJustify, "$insets": footerInsets, "$offsetTop": isLineChart ? X_AXIS_TITLE_GAP - LINE_CHART_X_LABELS_BLANK : 0, "$awaitingPlot": isAwaitingPlot, children: [jsxRuntime.jsx(ChartXAxisTitle, { children: xAxisTitle }), isLegendInline && jsxRuntime.jsx(ContainerLegend, { children: renderElement({ id: "legend" }) })] })) : null;
|
|
9589
|
+
// Легенда линейного графика: под подписью оси X — через X_AXIS_TITLE_LEGEND_GAP, без подписи — на её
|
|
9590
|
+
// месте, вплотную под подписями делений оси X. У остальных графиков — прежняя раскладка общей легенды.
|
|
9591
|
+
const legendRow = isLegendInline ? null : (jsxRuntime.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" }) }));
|
|
9592
|
+
return (jsxRuntime.jsxs(ContainerRoot, { ...root, children: [jsxRuntime.jsx(ContainerBackground, { elementConfig: elementConfig, renderElement: renderElement }), jsxRuntime.jsx(ExpandableTitle, { elementConfig: elementConfig, type: type, renderElement: renderElement }), isVisible && (jsxRuntime.jsxs(Container, { ...body, 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: [twoColumns && footer ? (jsxRuntime.jsxs(ChartWithFooter, { "$fill": !!fill, children: [chartSlot, footer] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [chartSlot, footer] })), legendRow] })) : (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2014" })) })] }))] }));
|
|
9175
9593
|
});
|
|
9176
9594
|
|
|
9177
9595
|
const InnerContainerWrapper = styled.div.withConfig({ displayName: "InnerContainerWrapper", componentId: "sc-1a1fl7q" }) `
|
|
@@ -9529,6 +9947,7 @@ const DEFAULT_BAR_CHART_HEIGHT = 100;
|
|
|
9529
9947
|
const BAR_CHART_HEADER_HEIGHT = 40;
|
|
9530
9948
|
const BAR_CHART_FOOTER_MARGIN = 16;
|
|
9531
9949
|
const LINE_CHART_LABEL_MARGIN = 20;
|
|
9950
|
+
const LINE_CHART_X_AXIS_PADDING = 15;
|
|
9532
9951
|
const DEFAULT_BAR_CHART_MARGINS = {
|
|
9533
9952
|
top: 0,
|
|
9534
9953
|
left: 0,
|
|
@@ -15192,9 +15611,9 @@ const ElementImage = React.memo(({ type, elementConfig }) => {
|
|
|
15192
15611
|
});
|
|
15193
15612
|
|
|
15194
15613
|
const ElementLegend = React.memo(({ type, element, elementConfig }) => {
|
|
15195
|
-
const { attributes,
|
|
15614
|
+
const { attributes, expandedContainers } = useWidgetContext(type);
|
|
15196
15615
|
const { options } = elementConfig || {};
|
|
15197
|
-
const { twoColumns, chartId, relatedDataSources, fontSize } = options || {};
|
|
15616
|
+
const { twoColumns, column, chartId, relatedDataSources, fontSize } = options || {};
|
|
15198
15617
|
const chartElement = React.useMemo(() => findAnd.returnFound(element?.children, { id: chartId }), [chartId, element?.children]);
|
|
15199
15618
|
const { data, loading } = useChartData({
|
|
15200
15619
|
element: chartElement,
|
|
@@ -15205,10 +15624,10 @@ const ElementLegend = React.memo(({ type, element, elementConfig }) => {
|
|
|
15205
15624
|
if (!data[0]?.items?.length)
|
|
15206
15625
|
return [];
|
|
15207
15626
|
if (chartElement?.options?.chartType === "line") {
|
|
15208
|
-
return axes.map(
|
|
15627
|
+
return axes.map(related => {
|
|
15209
15628
|
return {
|
|
15210
|
-
name: alias,
|
|
15211
|
-
color:
|
|
15629
|
+
name: related.alias,
|
|
15630
|
+
color: resolveAxis(related).color,
|
|
15212
15631
|
};
|
|
15213
15632
|
});
|
|
15214
15633
|
}
|
|
@@ -15222,7 +15641,7 @@ const ElementLegend = React.memo(({ type, element, elementConfig }) => {
|
|
|
15222
15641
|
});
|
|
15223
15642
|
}, [data, chartElement?.options?.chartType, relatedDataSources, axes, attributes]);
|
|
15224
15643
|
return !chartElement?.options?.expanded ||
|
|
15225
|
-
expandedContainers?.[chartElement.id] ? (jsxRuntime.jsx(ChartLegend, { data: legendData, loading: loading, chartElement: chartElement, twoColumns: twoColumns, fontSize: fontSize, type: type })) : null;
|
|
15644
|
+
expandedContainers?.[chartElement.id] ? (jsxRuntime.jsx(ChartLegend, { data: legendData, loading: loading, chartElement: chartElement, twoColumns: twoColumns, column: column, fontSize: fontSize, type: type })) : null;
|
|
15226
15645
|
});
|
|
15227
15646
|
|
|
15228
15647
|
const ExternalLink = styled(uilibGl.IconButton).attrs(() => ({
|
|
@@ -16790,7 +17209,11 @@ const getDataFromRelatedFeatures = ({ t, config, filters, relatedConfig, dataSou
|
|
|
16790
17209
|
if (isOtherSliced) {
|
|
16791
17210
|
data = data.slice(0, config?.options?.otherItems);
|
|
16792
17211
|
}
|
|
16793
|
-
|
|
17212
|
+
// Источник чарта отличается от источника фильтра наличием осей: BarChartFilter собирает
|
|
17213
|
+
// relatedConfig вручную и оси не задаёт, потому что ему нужна исходная палитра, а не растянутая
|
|
17214
|
+
// под число элементов. Гейт смотрит и на устаревший chartAxis — иначе у старых конфигов
|
|
17215
|
+
// молча поехали бы цвета столбцов и долей.
|
|
17216
|
+
const gradientArray = hasAxisConfig(relatedConfig)
|
|
16794
17217
|
? stretchPalette(colors, data.length)
|
|
16795
17218
|
: colors;
|
|
16796
17219
|
const filter = getConfigFilter(relatedConfig?.filterName, filters);
|
|
@@ -18856,6 +19279,15 @@ const StackBar = ({ data, filterName, type, alias, options, fill, renderElement,
|
|
|
18856
19279
|
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(alias || showTotal) && (jsxRuntime.jsxs(StackBarHeader, { children: [jsxRuntime.jsx(StackBarAlias, { children: renderElement({ id: "alias" }) }), showTotal && (jsxRuntime.jsxs(StackBarTotal, { children: [jsxRuntime.jsx(StackBarValue, { children: formatValue(total) }), !!units && jsxRuntime.jsx(StackBarUnits, { children: units })] }))] })), groupTooltip ? (jsxRuntime.jsx(uilibGl.Tooltip, { placement: "top", arrow: true, content: renderGroupTooltip, children: ref => (jsxRuntime.jsx(StackBarContainer, { ref: ref, "$fill": fill, children: renderItems })) })) : (jsxRuntime.jsx(StackBarContainer, { "$fill": fill, children: renderItems }))] }));
|
|
18857
19280
|
};
|
|
18858
19281
|
|
|
19282
|
+
/** Горизонтальные подписи осей над телом графика: левая прижата влево, правая — вправо. */
|
|
19283
|
+
const AxisTitlesTop = ({ left, right, neutralColor }) => (jsxRuntime.jsxs(AxisTitlesRow, { children: [jsxRuntime.jsx(AxisTitleTop, { "$color": left?.color || neutralColor, title: left?.title, children: left?.title }), jsxRuntime.jsx(AxisTitleTop, { "$color": right?.color || neutralColor, title: right?.title, children: right?.title })] }));
|
|
19284
|
+
/** Вертикальная подпись оси сбоку от тела графика. Без подписи ничего не рендерит. */
|
|
19285
|
+
const AxisTitleSideSlot = ({ side, axisTitle, neutralColor, plotTop, plotBottom, }) => {
|
|
19286
|
+
if (!axisTitle)
|
|
19287
|
+
return null;
|
|
19288
|
+
return (jsxRuntime.jsx(AxisTitleSideColumn, { "$side": side, "$plotTop": plotTop, "$plotBottom": plotBottom, children: jsxRuntime.jsx(AxisTitleSide, { "$side": side, "$color": axisTitle.color || neutralColor, title: axisTitle.title, children: axisTitle.title }) }));
|
|
19289
|
+
};
|
|
19290
|
+
|
|
18859
19291
|
const Chart = React.memo(({ config, element, elementConfig, type, renderElement }) => {
|
|
18860
19292
|
const theme = styled.useTheme();
|
|
18861
19293
|
const { palette } = theme;
|
|
@@ -18876,12 +19308,18 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
18876
19308
|
const radius = options?.radius || DEFAULT_BARCHART_RADIUS;
|
|
18877
19309
|
const padding = options?.padding || DEFAULT_BAR_PADDING;
|
|
18878
19310
|
const fontColor = options?.fontColor || palette.iconDisabled;
|
|
19311
|
+
// Нейтральная подпись оси — тем же цветом, что и значения шкалы (на макете у них один стиль).
|
|
19312
|
+
const axisTitleColor = options?.fontColor || palette.textDisabled;
|
|
18879
19313
|
const angle = options?.angle || DEFAULT_CHART_ANGLE;
|
|
18880
19314
|
const barWidth = options?.barWidth || DEFAULT_BAR_WIDTH;
|
|
18881
19315
|
const cornerRadius = options?.cornerRadius || 0;
|
|
18882
19316
|
const relatedAttributes = React.useMemo(() => relatedDataSources || [], [relatedDataSources]);
|
|
18883
|
-
const axes = React.useMemo(() => relatedAttributes.filter(
|
|
19317
|
+
const axes = React.useMemo(() => relatedAttributes.filter(isValueAxis), [relatedAttributes]);
|
|
18884
19318
|
const filterName = getChartFilterName(relatedDataSources);
|
|
19319
|
+
const { axisTitles, hasTopTitles, hasSideTitles, axisTitlesHeight, hideLeftAxis, hideRightAxis, } = useChartAxisTitles({ options, axes, isLineChart });
|
|
19320
|
+
// Ширина вертикальных подписей зависит от числа строк текста, поэтому тело между ними меряем
|
|
19321
|
+
// по факту, а не вычитаем константу.
|
|
19322
|
+
const [lineBodyRef, { width: measuredBodyWidth }] = useResizeBox(hasSideTitles);
|
|
18885
19323
|
const isRelated = !!relatedAttributes?.length;
|
|
18886
19324
|
const { data, loading } = useChartData({ element, type });
|
|
18887
19325
|
const totalWidthFromBarWidth = (data[0]?.items?.length || 0) * (barWidth + padding);
|
|
@@ -18909,17 +19347,54 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
18909
19347
|
const chartHeight = React.useMemo(() => (fitsHeight && measuredHeight ? measuredHeight : height), [fitsHeight, measuredHeight, height]);
|
|
18910
19348
|
// Круглый график вписываем по короткой стороне (object-fit: contain), центрируя в ячейке.
|
|
18911
19349
|
const pieSide = React.useMemo(() => (fitsHeight && measuredHeight ? Math.min(chartWidth, measuredHeight) : chartWidth), [fitsHeight, measuredHeight, chartWidth]);
|
|
19350
|
+
// Тело линейного графика — это ячейка за вычетом подписей осей. По высоте вычитаем только в
|
|
19351
|
+
// fill: вне fill chartHeight означает высоту самого тела, и обёртка честно растёт на подписи.
|
|
19352
|
+
const lineBodyWidth = React.useMemo(() => {
|
|
19353
|
+
if (!hasSideTitles)
|
|
19354
|
+
return chartWidth;
|
|
19355
|
+
if (measuredBodyWidth)
|
|
19356
|
+
return measuredBodyWidth;
|
|
19357
|
+
// До первого замера — оценка: одна строка текста и зазор на каждую подпись.
|
|
19358
|
+
const sideTitlesCount = Number(!!axisTitles.left) + Number(!!axisTitles.right);
|
|
19359
|
+
return Math.max(chartWidth - sideTitlesCount * (AXIS_TITLE_LINE_HEIGHT + AXIS_TITLE_GAP), 0);
|
|
19360
|
+
}, [hasSideTitles, measuredBodyWidth, chartWidth, axisTitles]);
|
|
19361
|
+
const lineBodyHeight = React.useMemo(() => (fitsHeight ? Math.max(chartHeight - axisTitlesHeight, 0) : chartHeight), [fitsHeight, chartHeight, axisTitlesHeight]);
|
|
19362
|
+
// Обёртка выше тела на строку горизонтальных подписей. В fill вся высота уже отмерена ячейкой,
|
|
19363
|
+
// поэтому прибавлять нечего — иначе тело вылезло бы за нижнюю границу.
|
|
19364
|
+
const lineWrapperHeight = React.useMemo(() => (fitsHeight ? chartHeight : chartHeight + axisTitlesHeight), [fitsHeight, chartHeight, axisTitlesHeight]);
|
|
18912
19365
|
const [customize] = useChartChange({
|
|
18913
19366
|
dataSources: config.dataSources,
|
|
18914
19367
|
chartId: elementConfig?.id,
|
|
18915
|
-
width:
|
|
18916
|
-
height:
|
|
19368
|
+
width: lineBodyWidth,
|
|
19369
|
+
height: lineBodyHeight,
|
|
18917
19370
|
fontColor,
|
|
18918
19371
|
relatedAttributes,
|
|
18919
19372
|
defaultColor: primaryColor,
|
|
18920
19373
|
markers: configMarkers,
|
|
18921
19374
|
showMarkers,
|
|
18922
19375
|
});
|
|
19376
|
+
const { formatLeftTick, formatRightTick } = useChartAxisTickFormat({
|
|
19377
|
+
dataSources: config.dataSources,
|
|
19378
|
+
relatedAttributes,
|
|
19379
|
+
});
|
|
19380
|
+
const { reportPlotInsets } = React.useContext(ChartPlotContext);
|
|
19381
|
+
// Положение поля графика известно только после отрисовки: ширину шкал вычисляет d3. Смещение тела
|
|
19382
|
+
// берём из offset-размеров, а не из getBoundingClientRect: они не искажаются масштабом превью и
|
|
19383
|
+
// transform предков, а координаты шкалы всегда в пикселях SVG.
|
|
19384
|
+
const customizeLine = React.useCallback(args => {
|
|
19385
|
+
customize?.(args);
|
|
19386
|
+
const body = lineBodyRef.current;
|
|
19387
|
+
const layout = body?.parentElement?.parentElement;
|
|
19388
|
+
if (!reportPlotInsets || !body || !layout)
|
|
19389
|
+
return;
|
|
19390
|
+
const bodyLeft = body.offsetLeft - layout.offsetLeft;
|
|
19391
|
+
const [plotLeft, plotRight] = args.xScale.range();
|
|
19392
|
+
reportPlotInsets({
|
|
19393
|
+
left: Math.round(bodyLeft + plotLeft),
|
|
19394
|
+
right: Math.round(layout.offsetWidth - bodyLeft - plotRight),
|
|
19395
|
+
width: layout.offsetWidth,
|
|
19396
|
+
});
|
|
19397
|
+
}, [customize, reportPlotInsets, lineBodyRef]);
|
|
18923
19398
|
const { formatFilterColor, onFilter } = useWidgetFilters(type, filterName, data?.[0]?.items);
|
|
18924
19399
|
const formattedAttributes = React.useMemo(() => getFormattedAttributes(t, (data[0]?.items || []), attributes, element), [attributes, data, element, t]);
|
|
18925
19400
|
const totalSum = React.useMemo(() => isRelated
|
|
@@ -18978,6 +19453,17 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
18978
19453
|
left: markersMargin,
|
|
18979
19454
|
};
|
|
18980
19455
|
}, [showMarkers, markers?.length]);
|
|
19456
|
+
// Значения шкалы барчарта d3 центрирует на делении, и крайние деления наполовину выступают за поле.
|
|
19457
|
+
// При нулевом поле SVG выступ уходит за SVG, где его срезает BarChartContainer (overflow-x: hidden,
|
|
19458
|
+
// отчего и по вертикали содержимое не visible). Место под выступ резервируем внутри SVG — внешние
|
|
19459
|
+
// размеры обёрток не меняются. Без значений шкалы (showLabels) выступать нечему.
|
|
19460
|
+
const barMargin = React.useMemo(() => showLabels
|
|
19461
|
+
? {
|
|
19462
|
+
...margin,
|
|
19463
|
+
top: Math.max(margin.top, Y_AXIS_LABEL_OVERHANG),
|
|
19464
|
+
bottom: Math.max(margin.bottom, Y_AXIS_LABEL_OVERHANG),
|
|
19465
|
+
}
|
|
19466
|
+
: margin, [showLabels, margin]);
|
|
18981
19467
|
const formatTooltipName = React.useCallback((name) => isRelated ? name : tooltipNameFromAttributes(name, formattedAttributes), [formattedAttributes, isRelated]);
|
|
18982
19468
|
const formatTooltipValue = React.useCallback((value, name) => isRelated
|
|
18983
19469
|
? tooltipValueFromRelatedFeatures(t, value, relatedAttributes, data[0]?.layerInfo)
|
|
@@ -19004,26 +19490,42 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
19004
19490
|
attributeUnits: item.attributeUnits,
|
|
19005
19491
|
dataSourceName: item.dataSourceName,
|
|
19006
19492
|
stroke: item.color || primaryColor,
|
|
19493
|
+
axis: item.axisSide ?? "left",
|
|
19007
19494
|
values: item.items
|
|
19008
19495
|
?.filter(({ value }) => !isEmptyValue(value))
|
|
19009
19496
|
?.map(({ value }) => Number(value)),
|
|
19010
19497
|
}));
|
|
19011
|
-
const isHidedY =
|
|
19012
|
-
const
|
|
19013
|
-
.
|
|
19498
|
+
const isHidedY = hideLeftAxis && hideRightAxis;
|
|
19499
|
+
const valuesOfSide = (side) => lineChartData
|
|
19500
|
+
.filter(({ axis }) => axis === side)
|
|
19014
19501
|
.map(({ values }) => values)
|
|
19015
19502
|
.flat();
|
|
19503
|
+
const leftValues = valuesOfSide("left");
|
|
19504
|
+
const rightValues = valuesOfSide("right");
|
|
19016
19505
|
// Резервируем нижнюю полосу под подписи оси X: без неё ось прижата к нижней границе,
|
|
19017
19506
|
// подписи (сдвинутые на +16px) вылезают за тело и режутся overflow: hidden у ChartFillMeasure.
|
|
19507
|
+
// Сверху резервируем выступ верхнего деления шкалы: оно центрировано на линии сетки и при нулевом
|
|
19508
|
+
// поле наполовину уходит за SVG, где в fill его срезает overflow: hidden у ChartFillMeasure.
|
|
19509
|
+
const topOverhang = Math.max(Y_AXIS_LABEL_OVERHANG - margin.top, 0);
|
|
19018
19510
|
const lineMargin = {
|
|
19019
19511
|
...margin,
|
|
19512
|
+
top: margin.top + topOverhang,
|
|
19020
19513
|
bottom: Math.max(margin.bottom, LINE_CHART_LABEL_MARGIN),
|
|
19021
19514
|
};
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19515
|
+
// draw.ts вычитает верхнее поле и из нижней границы поля графика — компенсируем добавленное,
|
|
19516
|
+
// чтобы низ поля и зазор до подписей оси X остались на месте.
|
|
19517
|
+
const lineXAxisPadding = LINE_CHART_X_AXIS_PADDING - topOverhang;
|
|
19518
|
+
// Колонки вертикальных подписей повторяют поля SVG, чтобы подпись центрировалась по полю
|
|
19519
|
+
// графика, а не по всей высоте вместе с подписями оси X.
|
|
19520
|
+
const plotTop = lineMargin.top;
|
|
19521
|
+
const plotBottom = lineMargin.top + lineMargin.bottom + lineXAxisPadding;
|
|
19522
|
+
return (jsxRuntime.jsxs(AnyChartWrapper, { height: lineWrapperHeight, children: [jsxRuntime.jsx(LineChartStyles, {}), jsxRuntime.jsxs(LineChartLayout, { children: [hasTopTitles && (jsxRuntime.jsx(AxisTitlesTop, { left: axisTitles.left, right: axisTitles.right, neutralColor: axisTitleColor })), jsxRuntime.jsxs(LineChartRow, { children: [hasSideTitles && (jsxRuntime.jsx(AxisTitleSideSlot, { side: "left", axisTitle: axisTitles.left, neutralColor: axisTitleColor, plotTop: plotTop, plotBottom: plotBottom })), jsxRuntime.jsx(LineChartBody, { ref: lineBodyRef, children: jsxRuntime.jsx(charts.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 => {
|
|
19523
|
+
// Обе оси приходят в один колбэк, поэтому сторону определяем по классу.
|
|
19524
|
+
const isRight = yAxis.classed(charts.lineChartClassNames.lineChartYScaleRight);
|
|
19525
|
+
if (isRight ? hideRightAxis : hideLeftAxis) {
|
|
19526
|
+
yAxis.remove();
|
|
19527
|
+
}
|
|
19528
|
+
}, 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 && (jsxRuntime.jsx(AxisTitleSideSlot, { side: "right", axisTitle: axisTitles.right, neutralColor: axisTitleColor, plotTop: plotTop, plotBottom: plotBottom }))] })] })] }));
|
|
19027
19529
|
}
|
|
19028
19530
|
if (isStackBar) {
|
|
19029
19531
|
// Вне fill chartHeight — высота самой полосы, и обёртка честно становится выше на строку
|
|
@@ -19053,7 +19555,7 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
19053
19555
|
const barRadius = options?.cornerRadius ?? DEFAULT_BARCHART_RADIUS;
|
|
19054
19556
|
bars.attr("rx", barRadius);
|
|
19055
19557
|
bars.attr("ry", barRadius);
|
|
19056
|
-
}, margin:
|
|
19558
|
+
}, margin: barMargin, xAxisPadding: 0, yAxisPadding: 0, formatTooltipValue: formatTooltipValue, formatTooltipName: formatTooltipName, hideTooltipGroupName: true, dynamicTooltipEnable: true, isBarTooltip: true, onBarClick: filterName ? item => onFilter(item.name) : undefined }) }) }));
|
|
19057
19559
|
}, [
|
|
19058
19560
|
customXAxisBottom,
|
|
19059
19561
|
customYAxisLeft,
|
|
@@ -19069,6 +19571,15 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
19069
19571
|
formatFilterColor,
|
|
19070
19572
|
chartWidth,
|
|
19071
19573
|
chartHeight,
|
|
19574
|
+
lineBodyWidth,
|
|
19575
|
+
lineBodyHeight,
|
|
19576
|
+
lineWrapperHeight,
|
|
19577
|
+
axisTitles,
|
|
19578
|
+
hasTopTitles,
|
|
19579
|
+
hasSideTitles,
|
|
19580
|
+
axisTitleColor,
|
|
19581
|
+
hideLeftAxis,
|
|
19582
|
+
hideRightAxis,
|
|
19072
19583
|
fitsHeight,
|
|
19073
19584
|
pieSide,
|
|
19074
19585
|
fill,
|
|
@@ -19078,11 +19589,14 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
19078
19589
|
formatTooltipValue,
|
|
19079
19590
|
formatTooltipName,
|
|
19080
19591
|
filterName,
|
|
19081
|
-
axes,
|
|
19082
19592
|
labels,
|
|
19083
19593
|
margin,
|
|
19594
|
+
barMargin,
|
|
19084
19595
|
renderLineChartTooltip,
|
|
19085
|
-
|
|
19596
|
+
customizeLine,
|
|
19597
|
+
lineBodyRef,
|
|
19598
|
+
formatLeftTick,
|
|
19599
|
+
formatRightTick,
|
|
19086
19600
|
primaryColor,
|
|
19087
19601
|
type,
|
|
19088
19602
|
elementConfig?.children,
|
|
@@ -19099,14 +19613,14 @@ const Chart = React.memo(({ config, element, elementConfig, type, renderElement
|
|
|
19099
19613
|
]);
|
|
19100
19614
|
if (!isVisibleContainer(id, expandable, expanded, expandedContainers))
|
|
19101
19615
|
return null;
|
|
19102
|
-
const chartBody = (jsxRuntime.jsxs(ChartWrapper, { width: isPieChart && fill ? pieSide : width, height: isStackBar ? "auto" : isPieChart ? (fill ? pieSide : width) : chartHeight, column: column, loading: loading, children: [jsxRuntime.jsx(ChartTooltipStyles, {}), renderChart] }));
|
|
19616
|
+
const chartBody = (jsxRuntime.jsxs(ChartWrapper, { width: isPieChart && fill ? pieSide : width, height: isStackBar ? "auto" : isPieChart ? (fill ? pieSide : width) : isLineChart ? lineWrapperHeight : chartHeight, column: column, loading: loading, children: [jsxRuntime.jsx(ChartTooltipStyles, {}), renderChart] }));
|
|
19103
19617
|
if (!fill)
|
|
19104
19618
|
return chartBody;
|
|
19105
19619
|
// fill: тело центрируется в измеряемой обёртке, ref которой питает ResizeObserver обеими осями.
|
|
19106
19620
|
return jsxRuntime.jsx(ChartFillMeasure, { ref: chartRef, children: chartBody });
|
|
19107
19621
|
});
|
|
19108
19622
|
|
|
19109
|
-
const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, loading }) => {
|
|
19623
|
+
const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, column, loading }) => {
|
|
19110
19624
|
const { t } = useGlobalContext();
|
|
19111
19625
|
const { sliceItems, showMore, onShowMore } = useShownOtherItems(chartElement?.options);
|
|
19112
19626
|
const axes = React.useMemo(() => getChartAxes(chartElement), [chartElement]);
|
|
@@ -19115,7 +19629,7 @@ const ChartLegend = ({ data, chartElement, fontSize, type, twoColumns, loading }
|
|
|
19115
19629
|
const showMoreBtn = !!shownItems && data.length > shownItems;
|
|
19116
19630
|
if (!loading && !data?.length && !!hideEmpty)
|
|
19117
19631
|
return null;
|
|
19118
|
-
return (jsxRuntime.jsxs(uilibGl.Flex, { flexDirection: "column", opacity: loading ? FILTERED_VALUE_OPACITY / 100 : 1, children: [jsxRuntime.jsx(ChartLegendContainer, { twoColumns: twoColumns, children: sliceItems(data)?.map(({ name, color }, index) => (jsxRuntime.jsxs(ChartLegendItem, { hasAnyFilter: hasAnyFilter, isFiltered: isFiltered(name), onClick: axes?.[0]?.filterName ? () => onFilter(name) : undefined, children: [jsxRuntime.jsx(ChartLegendColor, { color: color }), jsxRuntime.jsx(ChartLegendName, { "$fontSize": fontSize, "$fontColor": fontColor, children: name })] }, index))) }), showMoreBtn && (jsxRuntime.jsx(uilibGl.LegendToggler, { onClick: onShowMore, toggled: showMore, children: t("other", { ns: "dashboard", defaultValue: "Другое" }) }))] }));
|
|
19632
|
+
return (jsxRuntime.jsxs(uilibGl.Flex, { flexDirection: "column", opacity: loading ? FILTERED_VALUE_OPACITY / 100 : 1, children: [jsxRuntime.jsx(ChartLegendContainer, { twoColumns: twoColumns, "$column": column, children: sliceItems(data)?.map(({ name, color }, index) => (jsxRuntime.jsxs(ChartLegendItem, { hasAnyFilter: hasAnyFilter, isFiltered: isFiltered(name), onClick: axes?.[0]?.filterName ? () => onFilter(name) : undefined, children: [jsxRuntime.jsx(ChartLegendColor, { color: color }), jsxRuntime.jsx(ChartLegendName, { "$fontSize": fontSize, "$fontColor": fontColor, children: name })] }, index))) }), showMoreBtn && (jsxRuntime.jsx(uilibGl.LegendToggler, { onClick: onShowMore, toggled: showMore, children: t("other", { ns: "dashboard", defaultValue: "Другое" }) }))] }));
|
|
19119
19633
|
};
|
|
19120
19634
|
|
|
19121
19635
|
const ContainerChildren = React.memo(({ type, items, isColumn, isMain, renderElement }) => {
|
|
@@ -19973,6 +20487,10 @@ const DEFAULT_HEATMAP_STYLE = {
|
|
|
19973
20487
|
exports.ALIGNMENTS = ALIGNMENTS;
|
|
19974
20488
|
exports.ALIGN_ITEMS = ALIGN_ITEMS;
|
|
19975
20489
|
exports.ATTRIBUTE_ICON_ELEMENT_TYPES = ATTRIBUTE_ICON_ELEMENT_TYPES;
|
|
20490
|
+
exports.AXIS_TITLE_GAP = AXIS_TITLE_GAP;
|
|
20491
|
+
exports.AXIS_TITLE_LINE_HEIGHT = AXIS_TITLE_LINE_HEIGHT;
|
|
20492
|
+
exports.AXIS_TITLE_TOP_GRID_GAP = AXIS_TITLE_TOP_GRID_GAP;
|
|
20493
|
+
exports.AXIS_TITLE_TOP_HEIGHT = AXIS_TITLE_TOP_HEIGHT;
|
|
19976
20494
|
exports.AddButtonRow = AddButtonRow;
|
|
19977
20495
|
exports.AddFeatureButton = AddFeatureButton;
|
|
19978
20496
|
exports.AddFeatureContainer = AddFeatureContainer;
|
|
@@ -19982,6 +20500,7 @@ exports.AttributeGalleryContainer = AttributeGalleryContainer;
|
|
|
19982
20500
|
exports.AttributeLabel = AttributeLabel;
|
|
19983
20501
|
exports.BASE_CONTAINER_STYLE = BASE_CONTAINER_STYLE;
|
|
19984
20502
|
exports.BG_IMAGE_SLOT_ID = BG_IMAGE_SLOT_ID;
|
|
20503
|
+
exports.CHART_AXIS_ALIGN_JUSTIFY = CHART_AXIS_ALIGN_JUSTIFY;
|
|
19985
20504
|
exports.CHART_TYPES = CHART_TYPES;
|
|
19986
20505
|
exports.COMPACT_FRACTION_DIGITS = COMPACT_FRACTION_DIGITS;
|
|
19987
20506
|
exports.CONFIG_PAGES_ID = CONFIG_PAGES_ID;
|
|
@@ -20135,6 +20654,8 @@ exports.HiddenTitleItems = HiddenTitleItems;
|
|
|
20135
20654
|
exports.IconContainer = IconContainer;
|
|
20136
20655
|
exports.ImageContainer = ImageContainer;
|
|
20137
20656
|
exports.LEFT_PANEL_HEADER_HEIGHT = LEFT_PANEL_HEADER_HEIGHT;
|
|
20657
|
+
exports.LINE_CHART_X_LABELS_BLANK = LINE_CHART_X_LABELS_BLANK;
|
|
20658
|
+
exports.LINE_CHART_Y_TICKS = LINE_CHART_Y_TICKS;
|
|
20138
20659
|
exports.Layer = Layer;
|
|
20139
20660
|
exports.LayerDescription = LayerDescription;
|
|
20140
20661
|
exports.LayerGroupList = LayerGroupList;
|
|
@@ -20214,6 +20735,9 @@ exports.TwoColumnContainer = TwoColumnContainer;
|
|
|
20214
20735
|
exports.UploadContainer = UploadContainer;
|
|
20215
20736
|
exports.VIEW_MODES = VIEW_MODES;
|
|
20216
20737
|
exports.VoteContainer = VoteContainer;
|
|
20738
|
+
exports.X_AXIS_TITLE_GAP = X_AXIS_TITLE_GAP;
|
|
20739
|
+
exports.X_AXIS_TITLE_LEGEND_GAP = X_AXIS_TITLE_LEGEND_GAP;
|
|
20740
|
+
exports.Y_AXIS_LABEL_OVERHANG = Y_AXIS_LABEL_OVERHANG;
|
|
20217
20741
|
exports.ZOOM_FILTER_NAME = ZOOM_FILTER_NAME;
|
|
20218
20742
|
exports.addDataSource = addDataSource;
|
|
20219
20743
|
exports.addDataSources = addDataSources;
|
|
@@ -20273,6 +20797,7 @@ exports.getAttributeIconUrl = getAttributeIconUrl;
|
|
|
20273
20797
|
exports.getAttributeValue = getAttributeValue;
|
|
20274
20798
|
exports.getAttributesConfiguration = getAttributesConfiguration;
|
|
20275
20799
|
exports.getAverageTrackSize = getAverageTrackSize;
|
|
20800
|
+
exports.getAxisSide = getAxisSide;
|
|
20276
20801
|
exports.getChartAxes = getChartAxes;
|
|
20277
20802
|
exports.getChartFilterName = getChartFilterName;
|
|
20278
20803
|
exports.getChartMarkers = getChartMarkers;
|
|
@@ -20324,6 +20849,7 @@ exports.getTrackSize = getTrackSize;
|
|
|
20324
20849
|
exports.getTrackSizeKey = getTrackSizeKey;
|
|
20325
20850
|
exports.getTrackSizes = getTrackSizes;
|
|
20326
20851
|
exports.getWrapperSizeStyle = getWrapperSizeStyle;
|
|
20852
|
+
exports.hasAxisConfig = hasAxisConfig;
|
|
20327
20853
|
exports.hasContainerBgImage = hasContainerBgImage;
|
|
20328
20854
|
exports.hexToRgba = hexToRgba;
|
|
20329
20855
|
exports.isCrossOriginUrl = isCrossOriginUrl;
|
|
@@ -20340,8 +20866,10 @@ exports.isNotValidSelectedTab = isNotValidSelectedTab;
|
|
|
20340
20866
|
exports.isNumeric = isNumeric;
|
|
20341
20867
|
exports.isObject = isObject;
|
|
20342
20868
|
exports.isProxyService = isProxyService;
|
|
20869
|
+
exports.isRightAxis = isRightAxis;
|
|
20343
20870
|
exports.isRootOwningContainer = isRootOwningContainer;
|
|
20344
20871
|
exports.isTreeFilterValue = isTreeFilterValue;
|
|
20872
|
+
exports.isValueAxis = isValueAxis;
|
|
20345
20873
|
exports.isVisibleContainer = isVisibleContainer;
|
|
20346
20874
|
exports.mapNodeById = mapNodeById;
|
|
20347
20875
|
exports.mergeAttributeConfigurations = mergeAttributeConfigurations;
|
|
@@ -20357,6 +20885,7 @@ exports.pointOptions = pointOptions;
|
|
|
20357
20885
|
exports.removeDataSource = removeDataSource;
|
|
20358
20886
|
exports.removeTracks = removeTracks;
|
|
20359
20887
|
exports.replaceNodesByIds = replaceNodesByIds;
|
|
20888
|
+
exports.resolveAxis = resolveAxis;
|
|
20360
20889
|
exports.rgbToHex = rgbToHex;
|
|
20361
20890
|
exports.roundFr = roundFr;
|
|
20362
20891
|
exports.roundTotalSum = roundTotalSum;
|
|
@@ -20384,6 +20913,9 @@ exports.useAttachmentsView = useAttachmentsView;
|
|
|
20384
20913
|
exports.useAutoCompleteControl = useAutoCompleteControl;
|
|
20385
20914
|
exports.useBeforeSave = useBeforeSave;
|
|
20386
20915
|
exports.useBgImageHost = useBgImageHost;
|
|
20916
|
+
exports.useChartAxisAttributes = useChartAxisAttributes;
|
|
20917
|
+
exports.useChartAxisTickFormat = useChartAxisTickFormat;
|
|
20918
|
+
exports.useChartAxisTitles = useChartAxisTitles;
|
|
20387
20919
|
exports.useChartChange = useChartChange;
|
|
20388
20920
|
exports.useChartData = useChartData;
|
|
20389
20921
|
exports.useConfigDataSources = useConfigDataSources;
|