fix: adjust analytics chart layout
Some checks failed
CI/CD Pipeline / Quality Gate (push) Failing after 33s
CI/CD Pipeline / Build and Deploy Dev via Compose (push) Has been skipped

This commit is contained in:
Gleb Korotkiy
2026-06-21 13:25:44 +03:00
parent da273308f9
commit 099d7d3f1f
2 changed files with 94 additions and 56 deletions

View File

@@ -562,6 +562,10 @@ select {
align-content: start; align-content: start;
} }
.analytics-card--trend .analytics-card__content {
grid-template-rows: auto minmax(0, 1fr);
}
.analytics-card--industries .analytics-card__content { .analytics-card--industries .analytics-card__content {
align-content: start; align-content: start;
} }
@@ -613,35 +617,6 @@ select {
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.analytics-card__icon-button {
display: inline-flex;
width: 36px;
height: 36px;
flex: 0 0 auto;
align-items: center;
justify-content: center;
padding: 0;
border: 0;
border-radius: 10px;
background: transparent;
color: rgb(2 36 86 / 0.22);
cursor: pointer;
transition:
background-color 160ms ease,
color 160ms ease;
}
.analytics-card__icon-button:hover,
.analytics-card__icon-button:focus-visible {
background: var(--Infographic-BG-Cards, #f3f3f3);
color: var(--Infographic-Main-Dark-Blue, #022456);
}
.analytics-card__icon-button svg {
width: 20px;
height: 20px;
}
.composition-layout { .composition-layout {
display: grid; display: grid;
grid-template-columns: minmax(0, 280px) minmax(0, 1fr); grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
@@ -1442,25 +1417,59 @@ select {
} }
.trend-chart-box { .trend-chart-box {
height: 250px;
min-width: 0; min-width: 0;
min-height: 0;
height: 100%;
overflow: hidden; overflow: hidden;
} }
.trend-chart-box > * {
height: 100%;
}
.trend-chart-box .unovis-xy-container { .trend-chart-box .unovis-xy-container {
--vis-axis-tick-label-font-size: 14px; --vis-axis-tick-label-font-size: 14px;
height: 100% !important;
} }
.cluster-trend-chart-scroll { .cluster-trend-chart-scroll {
position: relative;
min-width: 0; min-width: 0;
height: 280px; min-height: 0;
height: 100%;
padding-bottom: 18px; padding-bottom: 18px;
} }
.cluster-trend-chart-scroll::before {
position: absolute;
z-index: 0;
top: 42px;
right: 6px;
bottom: 44px;
left: 6px;
pointer-events: none;
content: '';
background: repeating-linear-gradient(
to bottom,
rgb(2 36 86 / 0.11) 0,
rgb(2 36 86 / 0.11) 1px,
transparent 1px,
transparent 25%
);
}
.cluster-trend-chart-scroll [data-slot='scroll-area-viewport'] { .cluster-trend-chart-scroll [data-slot='scroll-area-viewport'] {
position: relative;
z-index: 1;
height: 100%;
padding-bottom: 12px; padding-bottom: 12px;
} }
.cluster-trend-chart-scroll [data-slot='scroll-area-viewport'] > * {
min-height: 100%;
height: 100%;
}
.cluster-trend-chart-scroll [data-slot='scroll-area-scrollbar'] { .cluster-trend-chart-scroll [data-slot='scroll-area-scrollbar'] {
right: 6px; right: 6px;
bottom: 4px; bottom: 4px;
@@ -1475,17 +1484,19 @@ select {
.cluster-trend-chart { .cluster-trend-chart {
display: grid; display: grid;
height: 250px; min-height: 0;
height: 100%;
grid-auto-flow: column; grid-auto-flow: column;
grid-auto-columns: minmax(104px, 1fr); grid-auto-columns: minmax(104px, 1fr);
gap: 18px; gap: 18px;
align-items: end; align-items: end;
padding: 6px 6px 0; padding: 42px 6px 0;
} }
.cluster-trend-chart__item { .cluster-trend-chart__item {
position: relative; position: relative;
display: grid; display: grid;
height: 100%;
min-width: 0; min-width: 0;
grid-template-rows: minmax(120px, 1fr) auto; grid-template-rows: minmax(120px, 1fr) auto;
align-items: end; align-items: end;
@@ -1521,7 +1532,8 @@ select {
.cluster-trend-chart__bar-wrap { .cluster-trend-chart__bar-wrap {
position: relative; position: relative;
display: flex; display: flex;
height: 150px; min-height: 150px;
height: 100%;
align-items: flex-end; align-items: flex-end;
} }

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { ArrowUp, Building2 } from 'lucide-vue-next' import { ArrowUp } from 'lucide-vue-next'
import type { ChartSeriesConfig } from '@gisp/ui-kit-shadcn-vue' import type { ChartSeriesConfig } from '@gisp/ui-kit-shadcn-vue'
import { import {
Card, Card,
@@ -196,6 +196,10 @@ const isRankingTableContentSized = computed(() =>
const clusterCreationTrendBlock = computed(() => const clusterCreationTrendBlock = computed(() =>
dashboard.trendBlock?.type === 'cluster_creation_dynamics' ? dashboard.trendBlock : null, dashboard.trendBlock?.type === 'cluster_creation_dynamics' ? dashboard.trendBlock : null,
) )
const clusterTrendScrollLeft = ref(0)
const clusterTrendVisiblePointsCount = 5
const clusterTrendColumnWidth = 128
const clusterTrendColumnGap = 18
const associationTypeTabs = computed(() => dashboard.filtersResponse.association_types) const associationTypeTabs = computed(() => dashboard.filtersResponse.association_types)
const visibleAssociationTypeTabs = computed(() => const visibleAssociationTypeTabs = computed(() =>
@@ -739,28 +743,56 @@ const getSingleIndustryWidth = (row: SingleTopIndustryRow): string =>
const getSingleIndustryColor = (block: SingleTopIndustriesBlock): string => const getSingleIndustryColor = (block: SingleTopIndustriesBlock): string =>
associationTypeColorMap[block.association_type] associationTypeColorMap[block.association_type]
const clusterCreationMaxValue = computed(() => const clusterTrendVisibleStartIndex = computed(() => {
Math.max( const pointsCount = clusterCreationTrendBlock.value?.points.length ?? 0
...(clusterCreationTrendBlock.value?.points ?? []).map((point) => const maxStartIndex = Math.max(0, pointsCount - clusterTrendVisiblePointsCount)
getNumericValue(point.clusters_count), const rawStartIndex = Math.floor(
), clusterTrendScrollLeft.value / (clusterTrendColumnWidth + clusterTrendColumnGap),
0, )
),
) return Math.max(0, Math.min(maxStartIndex, rawStartIndex))
})
const clusterCreationVisiblePoints = computed(() => {
const points = clusterCreationTrendBlock.value?.points ?? []
if (!points.length) {
return []
}
return points.slice(
clusterTrendVisibleStartIndex.value,
clusterTrendVisibleStartIndex.value + clusterTrendVisiblePointsCount,
)
})
const clusterCreationMaxValue = computed(() => {
const visiblePoints = clusterCreationVisiblePoints.value.length
? clusterCreationVisiblePoints.value
: (clusterCreationTrendBlock.value?.points ?? [])
return Math.max(...visiblePoints.map((point) => getNumericValue(point.clusters_count)), 0)
})
const getClusterCreationBarHeight = (value: number | undefined): string => const getClusterCreationBarHeight = (value: number | undefined): string =>
getProgressWidth(getNumericValue(value), clusterCreationMaxValue.value) getProgressWidth(getNumericValue(value), clusterCreationMaxValue.value)
const getClusterTrendMinWidth = (pointsCount: number): string => { const getClusterTrendMinWidth = (pointsCount: number): string => {
const visiblePointsCount = 5 if (pointsCount <= clusterTrendVisiblePointsCount) {
const columnWidth = 128
const columnGap = 18
if (pointsCount <= visiblePointsCount) {
return '100%' return '100%'
} }
return `${pointsCount * columnWidth + Math.max(0, pointsCount - 1) * columnGap}px` return `${pointsCount * clusterTrendColumnWidth + Math.max(0, pointsCount - 1) * clusterTrendColumnGap}px`
}
const updateClusterTrendScrollState = (event: Event) => {
const target = event.target as HTMLElement | null
if (!target?.matches('[data-slot="scroll-area-viewport"]')) {
return
}
clusterTrendScrollLeft.value = target.scrollLeft
} }
const formatGrowthPercent = (value: number | null | undefined): string => const formatGrowthPercent = (value: number | null | undefined): string =>
@@ -1221,6 +1253,7 @@ const formatAreaUnit = (unit: string): string => (unit === 'ha' ? 'га' : unit)
class="cluster-trend-chart-scroll" class="cluster-trend-chart-scroll"
type="auto" type="auto"
scrollbar-orientation="horizontal" scrollbar-orientation="horizontal"
@scroll.capture="updateClusterTrendScrollState"
> >
<div <div
class="cluster-trend-chart" class="cluster-trend-chart"
@@ -1278,13 +1311,6 @@ const formatAreaUnit = (unit: string): string => (unit === 'ha' ? 'га' : unit)
<CardContent class="analytics-card__content"> <CardContent class="analytics-card__content">
<header class="analytics-card__header"> <header class="analytics-card__header">
<h2 class="analytics-card__title">{{ dashboard.regionRanking.title }}</h2> <h2 class="analytics-card__title">{{ dashboard.regionRanking.title }}</h2>
<button
type="button"
class="analytics-card__icon-button"
aria-label="Открыть таблицу рейтинга"
>
<Building2 aria-hidden="true" />
</button>
</header> </header>
<ScrollArea <ScrollArea