From 1688ba1b50e900f30e41e8920c05cdf1a3db7d5a Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 18 Jul 2026 09:02:54 +0800 Subject: [PATCH] unify Semi UI query metric rails --- .../web/src/v2/pages/HistoryPage.test.tsx | 27 +- .../apps/web/src/v2/pages/HistoryPage.tsx | 63 ++- .../apps/web/src/v2/pages/StatisticsPage.tsx | 16 +- .../apps/web/src/v2/productionEntry.test.ts | 20 +- .../v2/shared/WorkspaceMetricRail.test.tsx | 26 + .../web/src/v2/shared/WorkspaceMetricRail.tsx | 48 ++ .../apps/web/src/v2/styles/workspace.css | 487 +++++++----------- 7 files changed, 345 insertions(+), 342 deletions(-) create mode 100644 vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.test.tsx create mode 100644 vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx diff --git a/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.test.tsx index 5eb504c9..2021eebb 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.test.tsx @@ -110,11 +110,14 @@ test('clears stale rows, charts, and evidence as soon as the history scope chang const view = renderPage(); expect((await screen.findAllByText('旧车牌')).length).toBeGreaterThan(0); expect(screen.queryByText('OLDVIN-evidence')).not.toBeInTheDocument(); - for (const className of ['v2-history-filter-card', 'v2-history-context-card', 'v2-history-table-card']) { + for (const className of ['v2-history-filter-card', 'v2-history-summary-rail', 'v2-history-table-card']) { expect(view.container.querySelector(`.${className}.semi-card`)).toBeInTheDocument(); } - expect(view.container.querySelector('.v2-history-context .v2-history-metrics')).toBeInTheDocument(); - expect(view.container.querySelector('.v2-history-context .v2-history-summary')).toBeInTheDocument(); + expect(view.container.querySelector('.v2-history-summary-rail')).toHaveAttribute('aria-label', '历史数据查询统计信息'); + expect(view.container.querySelector('.v2-history-summary-secondary.semi-card-group')).toBeInTheDocument(); + expect(view.container.querySelectorAll('.v2-history-summary-card.semi-card')).toHaveLength(4); + expect(view.container.querySelector('.v2-history-summary-card.is-primary .v2-history-summary-value')).toHaveTextContent('1'); + expect(screen.getByText('1 / 1 字段').closest('.semi-tag')).toBeInTheDocument(); expect(view.container.querySelector('.v2-history-result-actions.v2-workspace-panel-actions')).toBeInTheDocument(); expect(view.container.querySelector('.v2-history-evidence')).not.toBeInTheDocument(); expect(view.container.querySelector('.v2-history-workspace')).not.toHaveClass('is-inspector-open'); @@ -180,8 +183,8 @@ test('renders only selectable Semi history cards on mobile', async () => { expect(action).toHaveAttribute('aria-expanded', 'false'); expect(view.container.querySelector('.v2-history-table-scroll.is-mobile-scroll')).toHaveAttribute('aria-label', '历史明细列表,可上下滚动'); expect(view.container.querySelector('.v2-history-table-scroll.is-mobile-scroll')).toHaveAttribute('tabindex', '0'); - expect(view.container.querySelector('.v2-history-metric-scroll')).toHaveAttribute('aria-label', '当前显示字段,可左右滑动'); - expect(view.container.querySelector('.v2-history-metric-scroll')).toHaveAttribute('tabindex', '0'); + expect(view.container.querySelector('.v2-history-metric-scroll')).not.toBeInTheDocument(); + expect(view.container.querySelector('.v2-history-summary-rail')).toBeInTheDocument(); expect(view.container.querySelector('.v2-history-result-actions')).toHaveClass('is-mobile-actions'); expect(screen.queryByText('MOBILEVIN-evidence')).not.toBeInTheDocument(); expect((mocks.historyData.mock.calls[0]?.[0] as URLSearchParams).get('limit')).toBe('20'); @@ -238,13 +241,8 @@ test('opens a working column visibility panel and preserves non-hideable identit expect(await screen.findByRole('columnheader', { name: '速度 (km/h)' })).toBeInTheDocument(); expect(screen.queryByRole('columnheader', { name: 'SOC (%)' })).not.toBeInTheDocument(); - const metricScroll = view.container.querySelector('.v2-history-metric-scroll'); - const manageMetrics = screen.getByRole('button', { name: '管理字段' }); - expect(metricScroll).toBeInTheDocument(); - expect(metricScroll).toHaveAttribute('aria-label', '当前显示字段'); - expect(metricScroll?.contains(screen.getByLabelText(/已选字段 速度/))).toBe(true); - expect(metricScroll?.contains(manageMetrics)).toBe(false); - expect(manageMetrics.parentElement).toHaveClass('v2-history-metrics'); + expect(view.container.querySelector('.v2-history-metric-scroll')).not.toBeInTheDocument(); + expect(screen.getByText('1 / 2 字段').closest('.semi-tag')).toBeInTheDocument(); const columnSettingsButton = screen.getByRole('button', { name: '列设置' }); fireEvent.click(columnSettingsButton); @@ -326,8 +324,7 @@ test('uses selected chartable fields for trends and omits empty RAW evidence UI' expect((mocks.historySeries.mock.calls[mocks.historySeries.mock.calls.length - 1]?.[0] as URLSearchParams).get('metrics')).toBe('speedKmh,totalMileageKm'); expect(screen.queryByText('RAW 证据')).not.toBeInTheDocument(); - const mileageToggle = screen.getAllByLabelText(/已选字段 .*点击取消显示/).find((button) => button.textContent?.includes('总里程')); - expect(mileageToggle).toBeDefined(); - fireEvent.click(mileageToggle!); + fireEvent.click(screen.getByRole('button', { name: '列设置' })); + fireEvent.click(screen.getByRole('checkbox', { name: '总里程 (km)' })); await waitFor(() => expect((mocks.historySeries.mock.calls[mocks.historySeries.mock.calls.length - 1]?.[0] as URLSearchParams).get('metrics')).toBe('speedKmh')); }); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.tsx index 3c6dd673..6055d03b 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/HistoryPage.tsx @@ -11,6 +11,7 @@ import { InlineError, PanelEmpty, PanelLoading } from '../shared/AsyncState'; import { MonitorReturnBar } from '../shared/MonitorReturnBar'; import { TablePagination } from '../shared/TablePagination'; import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel'; +import { WorkspaceMetricRail } from '../shared/WorkspaceMetricRail'; import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader'; import { detailTriggerRow } from '../shared/detailTriggerRow'; import { LIVE_QUERY_POLICY, QUERY_MEMORY, retainPreviousPageWithinScope } from '../queryPolicy'; @@ -327,6 +328,32 @@ export default function HistoryPage() { const totalPages = Math.max(1, Math.ceil((result?.total ?? 0) / limit)); const page = Math.floor(offset / limit) + 1; const summarySources = resultSummary?.sources?.join('、') || '—'; + const summaryLoading = keywords.length > 0 && dataQuery.isFetching && !result; + const summaryPrimary = { + label: '结果行数', + value: summaryLoading ? '—' : (result?.total ?? 0).toLocaleString('zh-CN'), + note: summaryLoading ? '正在读取当前筛选范围' : `${resultSummary?.vehicleCount ?? 0} 辆车辆` + }; + const summarySecondary = [ + { + label: '车辆数', + value: summaryLoading ? '—' : String(resultSummary?.vehicleCount ?? 0), + note: `当前筛选 ${keywords.length} 个车辆标识`, + className: 'is-vehicles' + }, + { + label: '数据来源', + value: summaryLoading ? '—' : `${resultSummary?.sources?.length ?? 0} 个来源`, + note: summarySources, + className: 'is-sources' + }, + { + label: '查询耗时', + value: summaryLoading ? '—' : resultSummary ? `${resultSummary.queryDurationMs} ms` : '—', + note: `${visibleMetrics.length} / ${allMetrics.length} 个业务字段`, + className: 'is-duration' + } + ]; return
@@ -348,31 +375,15 @@ export default function HistoryPage() {
{exportAllowed ? : 只读}
- -
-
-
- 显示字段 - {visibleMetrics.length} / {allMetrics.length} -
-
- {visibleMetrics.map((metric) => toggleMetric(metric.key)} onKeyDown={(event) => { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleMetric(metric.key); } }} onClose={(_, event) => { event.stopPropagation(); toggleMetric(metric.key); }} key={metric.key}>{metric.label}{metric.unit ? ` (${metric.unit})` : ''})} - {allMetrics.length > visibleMetrics.length ? 另有 {allMetrics.length - visibleMetrics.length} 项可选 : !allMetrics.length ? 查询后加载可用指标 : null} -
- -
-
-
结果行数{result?.total?.toLocaleString('zh-CN') ?? '—'}
-
车辆数{resultSummary?.vehicleCount ?? '—'}
-
数据源{summarySources}
-
查询耗时{resultSummary ? `${resultSummary.queryDurationMs} ms` : '—'}
-
-
-
+ {keywords.length ? : null} {dataQuery.isError ? dataQuery.refetch()} /> : null}
@@ -381,7 +392,7 @@ export default function HistoryPage() { {visibleMetrics.length} / {allMetrics.length} 字段{resultSummary ? {resultSummary.queryDurationMs} ms : null}} actionsClassName={`v2-history-result-actions${mobileLayout ? ' is-mobile-actions' : ''}`} actions={<> diff --git a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx index c2c5d59f..d97ea40f 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/StatisticsPage.tsx @@ -1,5 +1,5 @@ import { IconArrowDown, IconArrowUp, IconClose, IconDownload, IconRefresh, IconSearch, IconSetting } from '@douyinfe/semi-icons'; -import { Button, Card, CardGroup, Empty, Input, SideSheet, Spin, Switch, Table, Tag } from '@douyinfe/semi-ui'; +import { Button, Card, Empty, Input, SideSheet, Spin, Switch, Table, Tag } from '@douyinfe/semi-ui'; import { useQuery } from '@tanstack/react-query'; import { type CSSProperties, FormEvent, memo, type RefObject, useEffect, useMemo, useRef, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; @@ -13,6 +13,7 @@ import { TablePagination } from '../shared/TablePagination'; import { VehicleCandidateList } from '../shared/VehicleCandidateList'; import { mergeVehicleCandidates } from '../shared/vehicleCandidates'; import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel'; +import { WorkspaceMetricRail } from '../shared/WorkspaceMetricRail'; import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader'; import { QUERY_MEMORY, retainPreviousPageWithinScope } from '../queryPolicy'; import { monitorReturnFromParams, preserveMonitorReturn } from '../routing/monitorContext'; @@ -249,10 +250,15 @@ function SummaryRail({ data, criteria, fleetTotal, loading }: { data?: MileageSt ]; const primary = items[2]; const secondary = [items[0], items[1], items[3]]; - return - {primary[0]}{primary[1]}{primary[2]} - {secondary.map(([label, value, note], index) => {label}{value}{note})} - ; + return ({ label, value, note, className: `is-${['fleet', 'range', 'average'][index]}` }))} + />; } type VehicleMileageMatrix = VehicleOption & { days: Map; sources: Map; totalMileageKm: number }; diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts index e2cead77..3cd984e9 100644 --- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts +++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts @@ -17,6 +17,7 @@ const profileSyncPanelSource = readFileSync(resolve(process.cwd(), 'src/v2/pages const sourceEvidenceSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/VehicleSourceEvidencePanel.tsx'), 'utf8'); const mobileFilterToggleSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/MobileFilterToggle.tsx'), 'utf8'); const workspaceFilterPanelSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/WorkspaceFilterPanel.tsx'), 'utf8'); +const workspaceMetricRailSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/WorkspaceMetricRail.tsx'), 'utf8'); const segmentedTabsSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/SegmentedTabs.tsx'), 'utf8'); const metricActionSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/MetricActionButton.tsx'), 'utf8'); const tablePaginationSource = readFileSync(resolve(process.cwd(), 'src/v2/shared/TablePagination.tsx'), 'utf8'); @@ -127,10 +128,13 @@ describe('V2 production entry', () => { expect(corePageSources.HistoryPage).toContain('v2-history-column-sidesheet'); expect(corePageSources.HistoryPage).toContain(''); + expect(corePageSources.HistoryPage).toContain(' { expect(corePageSources.TrackPage).not.toContain(' { expect(corePageSources.StatisticsPage).toContain(' { expect(corePageSources[name]).toContain("from '../shared/MetricActionButton'"); expect(corePageSources[name]).toContain(' { + it('renders one primary outcome and supporting facts as accessible Semi cards', () => { + const view = render(); + + expect(view.container.querySelector('.test-rail.semi-card')).toHaveAttribute('aria-label', '查询统计'); + expect(view.container.querySelector('.test-card.is-primary .test-primary-value')).toHaveTextContent('1,024'); + expect(view.container.querySelector('.test-secondary.semi-card-group')).toBeInTheDocument(); + expect(view.container.querySelectorAll('.test-card.semi-card')).toHaveLength(3); + expect(screen.getByLabelText('车辆数:20;当前授权范围')).toHaveClass('is-vehicles'); + }); +}); diff --git a/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx new file mode 100644 index 00000000..bb91569f --- /dev/null +++ b/vehicle-data-platform/apps/web/src/v2/shared/WorkspaceMetricRail.tsx @@ -0,0 +1,48 @@ +import { Card, CardGroup } from '@douyinfe/semi-ui'; +import type { ReactNode } from 'react'; + +export type WorkspaceMetricRailItem = { + label: string; + value: ReactNode; + note: string; + className?: string; +}; + +export function WorkspaceMetricRail({ + ariaLabel, + primary, + secondary, + className, + cardClassName, + secondaryClassName, + primaryValueClassName +}: { + ariaLabel: string; + primary: WorkspaceMetricRailItem; + secondary: WorkspaceMetricRailItem[]; + className: string; + cardClassName: string; + secondaryClassName: string; + primaryValueClassName?: string; +}) { + const cardClass = (item: WorkspaceMetricRailItem, primaryCard = false) => [ + cardClassName, + primaryCard ? 'is-primary' : '', + item.className ?? '' + ].filter(Boolean).join(' '); + + return + + {primary.label} + {primary.value} + {primary.note} + + + {secondary.map((item) => + {item.label} + {item.value} + {item.note} + )} + + ; +} diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css index 564dfe8b..cc71c32f 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -645,31 +645,6 @@ font-size: 13px; } - .v2-history-metrics-card.semi-card { - min-height: 50px; - border-radius: 9px; - } - - .v2-history-metrics { - min-height: 50px; - gap: 9px; - padding: 8px 14px; - } - - .v2-history-metrics > strong { - font-size: 12px; - } - - .v2-history-metric-tag.semi-tag { - height: 30px; - border-radius: 7px; - font-size: 11px; - } - - .v2-history-metric-scroll > span { - font-size: 11px; - } - .v2-history-workspace { grid-template-columns: minmax(0, 1fr); gap: 12px; @@ -6016,129 +5991,6 @@ * Semi UI history workspace refinement. * Keep one query surface and one evidence surface; the table owns the viewport. */ -.v2-history-context-card.semi-card { - min-width: 0; - min-height: 78px; - flex: 0 0 auto; - overflow: hidden; - border: 1px solid #dce5ef; - border-radius: 10px; - background: #fff; - box-shadow: 0 6px 20px rgba(31, 47, 70, .045); -} - -.v2-history-context-card > .semi-card-body { - height: 100%; -} - -.v2-history-context { - display: grid; - min-width: 0; - min-height: 78px; - grid-template-columns: minmax(360px, 1.2fr) minmax(440px, .8fr); -} - -.v2-history-context .v2-history-metrics { - display: grid; - min-width: 0; - min-height: 78px; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 12px; - border-right: 1px solid #e5ebf2; - background: linear-gradient(90deg, #f8fbff 0%, #fff 30%); - padding: 12px 14px; -} - -.v2-history-metrics-heading { - display: flex; - flex-direction: column; - gap: 4px; - white-space: nowrap; -} - -.v2-history-metrics-heading > strong { - color: #30445d; - font-size: 12px; - font-weight: 720; -} - -.v2-history-metrics-heading > small { - color: #8795a8; - font-size: 10px; - font-variant-numeric: tabular-nums; -} - -.v2-history-context .v2-history-metric-tag.semi-tag { - height: 30px; - border-color: #c2d8f8; - border-radius: 7px; - background: #edf5ff; - font-size: 11px; -} - -.v2-history-context .v2-history-metrics-manage.semi-button { - min-width: 88px; - min-height: 34px; - flex: 0 0 auto; - border: 1px solid #d9e4f1; - border-radius: 8px; - background: #fff; - color: #526983; - font-size: 11px; -} - -.v2-history-context .v2-history-summary { - display: grid; - min-width: 0; - min-height: 78px; - grid-template-columns: 1.05fr .75fr 1.45fr .8fr; -} - -.v2-history-context .v2-history-summary > div { - position: relative; - display: flex; - min-width: 0; - justify-content: center; - flex-direction: column; - padding: 10px 13px; -} - -.v2-history-context .v2-history-summary > div + div::before { - position: absolute; - inset: 17px auto 17px 0; - width: 1px; - background: #e5ebf2; - content: ''; -} - -.v2-history-context .v2-history-summary small { - color: #7c8a9d; - font-size: 10px; - white-space: nowrap; -} - -.v2-history-context .v2-history-summary strong { - margin-top: 6px; - overflow: hidden; - color: #27384f; - font-size: 17px; - font-variant-numeric: tabular-nums; - font-weight: 760; - line-height: 1.1; - text-overflow: ellipsis; - white-space: nowrap; -} - -.v2-history-context .v2-history-summary .is-primary strong { - color: #1268df; - font-size: 21px; -} - -.v2-history-context .v2-history-summary strong.is-source { - font-size: 12px; -} - .v2-history-main { display: flex; min-width: 0; @@ -6361,31 +6213,6 @@ } } -@media (max-width: 1260px) and (min-width: 681px) { - .v2-history-context { - grid-template-columns: minmax(300px, 1fr) minmax(390px, .95fr); - } - - .v2-history-context .v2-history-metrics { - gap: 8px; - padding-inline: 11px; - } - - .v2-history-context .v2-history-metrics-manage.semi-button { - min-width: 34px; - width: 34px; - padding: 0; - } - - .v2-history-context .v2-history-metrics-manage .semi-button-content-right { - display: none; - } - - .v2-history-context .v2-history-summary > div { - padding-inline: 9px; - } -} - @media (max-height: 800px) and (min-width: 901px) { .v2-history-page { height: 100%; @@ -6400,25 +6227,6 @@ } @media (max-width: 900px) and (min-width: 681px) { - .v2-history-context { - grid-template-columns: 1fr; - } - - .v2-history-context .v2-history-metrics { - min-height: 58px; - border-right: 0; - border-bottom: 1px solid #e5ebf2; - } - - .v2-history-context .v2-history-summary { - min-height: 66px; - } - - .v2-history-context-card.semi-card, - .v2-history-context { - min-height: 124px; - } - .v2-history-workspace.is-inspector-open { grid-template-columns: minmax(0, 1fr) 320px; } @@ -6433,104 +6241,6 @@ padding: 8px; } - .v2-history-context-card.semi-card { - min-height: 126px; - border-radius: 11px; - } - - .v2-history-context { - display: flex; - min-height: 126px; - flex-direction: column; - } - - .v2-history-context .v2-history-metrics { - min-height: 68px; - grid-template-columns: minmax(0, 1fr) auto; - grid-template-rows: auto auto; - gap: 6px 8px; - border-right: 0; - border-bottom: 1px solid #e5ebf2; - padding: 7px 9px 8px; - } - - .v2-history-metrics-heading { - grid-column: 1; - grid-row: 1; - align-items: baseline; - flex-direction: row; - gap: 6px; - } - - .v2-history-metric-scroll { - width: 100%; - grid-column: 1 / -1; - grid-row: 2; - padding: 1px 12px 1px 0; - scroll-padding-inline: 0 12px; - scroll-snap-type: inline proximity; - scrollbar-width: none; - } - - .v2-history-metric-scroll::-webkit-scrollbar { - display: none; - } - - .v2-history-metrics-heading > strong { - font-size: 11px; - } - - .v2-history-metrics-heading > small { - font-size: 9px; - } - - .v2-history-context .v2-history-metric-tag.semi-tag { - height: 28px; - scroll-snap-align: start; - font-size: 10px; - } - - .v2-history-context .v2-history-metrics-manage.semi-button { - width: auto; - min-width: 76px; - min-height: 30px; - grid-column: 2; - grid-row: 1; - padding: 0 8px; - } - - .v2-history-context .v2-history-metrics-manage .semi-button-content-right { - display: inline; - } - - .v2-history-context .v2-history-summary { - min-height: 58px; - flex: 1; - grid-template-columns: 1.1fr .72fr 1.18fr .78fr; - } - - .v2-history-context .v2-history-summary > div { - padding: 8px 7px; - } - - .v2-history-context .v2-history-summary > div + div::before { - inset-block: 13px; - } - - .v2-history-context .v2-history-summary small { - font-size: 9px; - } - - .v2-history-context .v2-history-summary strong, - .v2-history-context .v2-history-summary .is-primary strong { - margin-top: 4px; - font-size: 15px; - } - - .v2-history-context .v2-history-summary strong.is-source { - font-size: 9px; - } - .v2-history-main { display: flex; height: 100%; @@ -10858,3 +10568,200 @@ font-size: 11px; } } + +/* + * Shared Semi UI metric rail applied to history data. + * The rail mirrors the mileage workspace: one primary outcome, then three + * supporting facts. Empty history screens omit the rail entirely. + */ +.v2-history-summary-rail.semi-card { + min-width: 0; + min-height: 88px; + flex: 0 0 auto; + overflow: hidden; + border: 1px solid #d8e3ef; + border-radius: 12px; + background: #fff; + box-shadow: 0 8px 26px rgba(31, 53, 80, .05); +} + +.v2-history-summary-rail > .semi-card-body { + display: grid; + min-height: 88px; + grid-template-columns: minmax(280px, .78fr) minmax(0, 1.72fr); +} + +.v2-history-summary-card.semi-card { + min-width: 0; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; +} + +.v2-history-summary-card > .semi-card-body { + position: relative; + display: flex; + min-width: 0; + height: 100%; + justify-content: center; + flex-direction: column; + padding: 11px 16px !important; +} + +.v2-history-summary-card.is-primary > .semi-card-body { + overflow: hidden; + background: linear-gradient(128deg, #0c61e7 0%, #1c73ee 68%, #2c82ef 100%); +} + +.v2-history-summary-card.is-primary > .semi-card-body::after { + position: absolute; + top: -80px; + right: -44px; + width: 180px; + height: 180px; + border: 26px solid rgba(255, 255, 255, .08); + border-radius: 50%; + content: ""; + pointer-events: none; +} + +.v2-history-summary-card small, +.v2-history-summary-card span { + position: relative; + z-index: 1; + overflow: hidden; + color: #7c8a9d; + font-size: 10px; + line-height: 1.35; + text-overflow: ellipsis; + white-space: nowrap; +} + +.v2-history-summary-card strong { + position: relative; + z-index: 1; + margin: 5px 0 4px; + overflow: hidden; + color: #22354d; + font-size: clamp(18px, 1.45vw, 24px); + font-variant-numeric: tabular-nums; + font-weight: 760; + line-height: 1.08; + text-overflow: ellipsis; + white-space: nowrap; +} + +.v2-history-summary-card.is-primary :is(small,strong,span) { + color: #fff; +} + +.v2-history-summary-card.is-primary small, +.v2-history-summary-card.is-primary span { + color: rgba(255, 255, 255, .72); +} + +.v2-history-summary-card.is-primary .v2-history-summary-value { + font-size: clamp(25px, 2.1vw, 34px); + letter-spacing: -.035em; +} + +.v2-history-summary-secondary.semi-card-group { + display: grid; + min-width: 0; + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.v2-history-summary-secondary > .v2-history-summary-card + .v2-history-summary-card > .semi-card-body::before { + position: absolute; + inset: 18px auto 18px 0; + width: 1px; + background: #e3eaf2; + content: ""; +} + +.v2-history-summary-card.is-sources strong { + font-size: clamp(13px, 1.05vw, 17px); +} + +.v2-history-result-meta { + display: inline-flex; + min-width: 0; + align-items: center; + gap: 7px; + color: #8090a3; + font-size: 10px; + font-variant-numeric: tabular-nums; +} + +@media (max-width: 900px) and (min-width: 681px) { + .v2-history-summary-rail > .semi-card-body { + grid-template-columns: minmax(230px, .7fr) minmax(0, 1.6fr); + } + + .v2-history-summary-card > .semi-card-body { + padding-inline: 12px !important; + } +} + +@media (max-width: 680px) { + .v2-history-summary-rail.semi-card { + min-height: 132px; + overflow: visible; + border: 0; + border-radius: 0; + background: transparent; + box-shadow: none; + } + + .v2-history-summary-rail > .semi-card-body { + min-height: 132px; + grid-template-columns: 1fr; + grid-template-rows: 70px 58px; + gap: 4px; + } + + .v2-history-summary-card.is-primary.semi-card, + .v2-history-summary-card.is-primary > .semi-card-body { + min-height: 70px; + border-radius: 10px; + } + + .v2-history-summary-card.is-primary > .semi-card-body { + padding: 7px 13px 8px !important; + } + + .v2-history-summary-card.is-primary .v2-history-summary-value { + margin-block: 2px 1px; + font-size: 24px; + line-height: 27px; + } + + .v2-history-summary-secondary.semi-card-group { + min-height: 58px; + overflow: hidden; + border: 1px solid #dfe7f0; + border-radius: 10px; + background: #fff; + } + + .v2-history-summary-secondary .v2-history-summary-card > .semi-card-body { + min-height: 58px; + padding: 7px 8px !important; + } + + .v2-history-summary-secondary > .v2-history-summary-card + .v2-history-summary-card > .semi-card-body::before { + inset-block: 8px; + } + + .v2-history-summary-secondary .v2-history-summary-card small, + .v2-history-summary-secondary .v2-history-summary-card span { + font-size: 8px; + } + + .v2-history-summary-secondary .v2-history-summary-card strong, + .v2-history-summary-secondary .v2-history-summary-card.is-sources strong { + margin-block: 2px 1px; + font-size: 14px; + } +}