1
This commit is contained in:
1 parent
4afad5fe10
commit
36fee1f291
65 files changed
+2933
-137
No files matched your search
+332
-69
@@ -1,101 +1,364 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { AgCharts } from 'ag-charts-react';
|
||||
import type { AgCartesianChartOptions } from 'ag-charts-community';
|
||||
import { ModuleRegistry } from 'ag-charts-community';
|
||||
import {
|
||||
AnnotationsModule,
|
||||
ContextMenuModule,
|
||||
CrosshairModule,
|
||||
LicenseManager,
|
||||
ZoomModule,
|
||||
} from 'ag-charts-enterprise';
|
||||
import { AG_CHARTS_LOCALE_ZH_CN } from 'ag-charts-locale';
|
||||
|
||||
const revenueData = [
|
||||
{ month: '1月', revenue: 146, orders: 92, margin: 36 },
|
||||
{ month: '2月', revenue: 158, orders: 101, margin: 39 },
|
||||
{ month: '3月', revenue: 171, orders: 117, margin: 43 },
|
||||
{ month: '4月', revenue: 185, orders: 126, margin: 45 },
|
||||
{ month: '5月', revenue: 193, orders: 133, margin: 47 },
|
||||
{ month: '6月', revenue: 221, orders: 148, margin: 52 },
|
||||
LicenseManager.setLicenseKey('[v3][RELEASE][0102]_NDg2Njc4MzY3MDgzNw==16d78ca762fb5d2ff740aed081e2af7b');
|
||||
ModuleRegistry.registerModules([AnnotationsModule, ContextMenuModule, ZoomModule, CrosshairModule]);
|
||||
|
||||
const yearlyCostData = [
|
||||
{ year: '2021', cost: 2200000, buildingArea: 18600, builtArea: 17200, usableArea: 14800 },
|
||||
{ year: '2022', cost: 2500000, buildingArea: 19100, builtArea: 17600, usableArea: 15300 },
|
||||
{ year: '2023', cost: 3450000, buildingArea: 20600, builtArea: 19000, usableArea: 16400 },
|
||||
{ year: '2024', cost: 9600000, buildingArea: 24800, builtArea: 23200, usableArea: 20100 },
|
||||
{ year: '2025', cost: null, buildingArea: null, builtArea: null, usableArea: null },
|
||||
];
|
||||
|
||||
function App() {
|
||||
const [chartType, setChartType] = useState<'bar' | 'line'>('bar');
|
||||
const metricOptions = [
|
||||
{ key: 'cost', label: '造价(元)', shortLabel: '造价' },
|
||||
{ key: 'buildingArea', label: '建筑面积指标(元/m²)', shortLabel: '建筑' },
|
||||
{ key: 'builtArea', label: '建造面积指标(元/m²)', shortLabel: '建造' },
|
||||
{ key: 'usableArea', label: '使用面积指标(元/m²)', shortLabel: '使用' },
|
||||
] as const;
|
||||
|
||||
const chartOptions = useMemo<AgCartesianChartOptions>(
|
||||
() => ({
|
||||
title: {
|
||||
text: '业务趋势',
|
||||
type MetricKey = (typeof metricOptions)[number]['key'];
|
||||
|
||||
function formatWan(value: number) {
|
||||
return `${Math.round(value / 10000).toLocaleString('zh-CN')}万`;
|
||||
}
|
||||
|
||||
function App() {
|
||||
const workspaceRef = useRef<HTMLElement>(null);
|
||||
const chartFrameRef = useRef<HTMLDivElement>(null);
|
||||
const [metricKey, setMetricKey] = useState<MetricKey>('cost');
|
||||
const [metricMenuOpen, setMetricMenuOpen] = useState(false);
|
||||
|
||||
const selectedMetric = metricOptions.find((option) => option.key === metricKey) ?? metricOptions[0];
|
||||
|
||||
useEffect(() => {
|
||||
const frame = chartFrameRef.current;
|
||||
const fullscreenTarget = workspaceRef.current;
|
||||
if (!frame || !fullscreenTarget) return;
|
||||
|
||||
const getFullscreenButton = () => frame.querySelector<HTMLButtonElement>('.chart-fullscreen-button');
|
||||
|
||||
const syncFullscreenButton = () => {
|
||||
const button = getFullscreenButton();
|
||||
if (!button) return;
|
||||
|
||||
let icon = button.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen');
|
||||
if (!icon) {
|
||||
button.innerHTML = '<i class="anticon anticon-arrow-salt ag-charts-myButton-fullScreen ag-charts-diy-button"></i>';
|
||||
icon = button.querySelector<HTMLElement>('.ag-charts-myButton-fullScreen');
|
||||
}
|
||||
|
||||
const isFullscreen = document.fullscreenElement === fullscreenTarget;
|
||||
button.classList.toggle('ag-charts-toolbar__button--active', isFullscreen);
|
||||
icon?.classList.toggle('anticon-arrow-salt', !isFullscreen);
|
||||
icon?.classList.toggle('anticon-shrink', isFullscreen);
|
||||
};
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
if (document.fullscreenElement === fullscreenTarget) {
|
||||
void document.exitFullscreen();
|
||||
} else {
|
||||
void fullscreenTarget.requestFullscreen();
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== 'F11') return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFullscreen();
|
||||
};
|
||||
|
||||
const handleFullscreenChange = () => {
|
||||
syncFullscreenButton();
|
||||
};
|
||||
|
||||
const handleToolbarClick = (event: MouseEvent) => {
|
||||
const target = event.target as Element | null;
|
||||
const button = target?.closest<HTMLButtonElement>(
|
||||
'.chart-fullscreen-button',
|
||||
);
|
||||
if (!button || !frame.contains(button)) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFullscreen();
|
||||
};
|
||||
|
||||
const handleToolbarKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.key !== ' ' && event.key !== 'Enter') return;
|
||||
|
||||
const target = event.target as Element | null;
|
||||
const button = target?.closest<HTMLButtonElement>(
|
||||
'.chart-fullscreen-button',
|
||||
);
|
||||
if (!button || !frame.contains(button)) return;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
toggleFullscreen();
|
||||
};
|
||||
|
||||
const suppressBrowserContextMenu = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
const observer = new MutationObserver(syncFullscreenButton);
|
||||
document.addEventListener('keydown', handleKeyDown, true);
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.addEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
frame.addEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
frame.addEventListener('click', handleToolbarClick, true);
|
||||
frame.addEventListener('keydown', handleToolbarKeyDown, true);
|
||||
observer.observe(frame, { childList: true, subtree: true });
|
||||
syncFullscreenButton();
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', handleKeyDown, true);
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
document.removeEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
frame.removeEventListener('contextmenu', suppressBrowserContextMenu);
|
||||
frame.removeEventListener('click', handleToolbarClick, true);
|
||||
frame.removeEventListener('keydown', handleToolbarKeyDown, true);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const chartOptions = useMemo<AgCartesianChartOptions>(() => {
|
||||
const isCost = metricKey === 'cost';
|
||||
const chartData = yearlyCostData.map((datum) => ({
|
||||
year: datum.year,
|
||||
amount: datum.cost == null ? null : isCost ? datum.cost : Math.round((datum.cost / Number(datum[metricKey])) * 100) / 100,
|
||||
}));
|
||||
|
||||
return {
|
||||
theme: {
|
||||
palette: {
|
||||
fills: ['#006f9b', '#ff7faa', '#00994d', '#ff8833', '#00a0dd'],
|
||||
strokes: ['#003f58', '#934962', '#004a25', '#914d1d', '#006288'],
|
||||
},
|
||||
params: {
|
||||
foregroundColor: '#262a33',
|
||||
backgroundColor: '#fff1e5',
|
||||
accentColor: '#0d7680',
|
||||
fontFamily: '"Microsoft YaHei", "PingFang SC", "Segoe UI", Arial, sans-serif',
|
||||
fontSize: 14,
|
||||
tooltipBackgroundColor: '#fff7ef',
|
||||
tooltipTextColor: '#262a33',
|
||||
},
|
||||
},
|
||||
subtitle: {
|
||||
text: '收入、订单与毛利表现',
|
||||
locale: {
|
||||
localeText: AG_CHARTS_LOCALE_ZH_CN,
|
||||
},
|
||||
background: {
|
||||
fill: 'transparent',
|
||||
},
|
||||
padding: {
|
||||
top: 16,
|
||||
right: 16,
|
||||
bottom: 18,
|
||||
left: 24,
|
||||
},
|
||||
data: chartData,
|
||||
zoom: {
|
||||
enabled: true,
|
||||
anchorPointX: 'pointer',
|
||||
anchorPointY: 'pointer',
|
||||
buttons: {
|
||||
enabled: true,
|
||||
visible: 'hover',
|
||||
buttons: [
|
||||
{ icon: 'zoom-out', value: 'zoom-out', section: 'zoom', tooltip: '缩小' },
|
||||
{ icon: 'zoom-in', value: 'zoom-in', section: 'zoom', tooltip: '放大' },
|
||||
{ icon: 'pan-left', value: 'pan-left', section: 'pan', tooltip: '左移' },
|
||||
{ icon: 'pan-right', value: 'pan-right', section: 'pan', tooltip: '右移' },
|
||||
{ icon: 'reset', value: 'reset', section: 'reset', tooltip: '重置' },
|
||||
],
|
||||
},
|
||||
},
|
||||
contextMenu: {
|
||||
enabled: true,
|
||||
items: ['defaults'],
|
||||
},
|
||||
annotations: {
|
||||
enabled: true,
|
||||
toolbar: {
|
||||
buttons: [
|
||||
{
|
||||
icon: 'trend-line-drawing',
|
||||
value: 'line-menu',
|
||||
tooltip: 'Line Tool',
|
||||
},
|
||||
{
|
||||
icon: 'text-annotation',
|
||||
value: 'text-menu',
|
||||
tooltip: 'Text Tool',
|
||||
},
|
||||
{
|
||||
icon: 'arrow-drawing',
|
||||
value: 'shape-menu',
|
||||
tooltip: 'Shape Tool',
|
||||
},
|
||||
{
|
||||
icon: 'fibonacci-retracement-drawing',
|
||||
value: 'fibonacci-menu',
|
||||
tooltip: 'Fibonacci Tool',
|
||||
},
|
||||
{
|
||||
icon: 'delete',
|
||||
value: 'clear',
|
||||
tooltip: 'Clear annotations',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
data: revenueData,
|
||||
series: [
|
||||
{
|
||||
type: chartType,
|
||||
xKey: 'month',
|
||||
yKey: 'revenue',
|
||||
yName: '收入',
|
||||
fill: '#2563eb',
|
||||
stroke: '#2563eb',
|
||||
},
|
||||
{
|
||||
type: chartType,
|
||||
xKey: 'month',
|
||||
yKey: 'orders',
|
||||
yName: '订单',
|
||||
fill: '#059669',
|
||||
stroke: '#059669',
|
||||
},
|
||||
{
|
||||
type: chartType,
|
||||
xKey: 'month',
|
||||
yKey: 'margin',
|
||||
yName: '毛利',
|
||||
fill: '#d97706',
|
||||
stroke: '#d97706',
|
||||
type: 'line',
|
||||
xKey: 'year',
|
||||
yKey: 'amount',
|
||||
yName: selectedMetric.label,
|
||||
stroke: '#0078a8',
|
||||
strokeWidth: 2,
|
||||
marker: {
|
||||
enabled: true,
|
||||
fill: '#0078a8',
|
||||
stroke: '#0078a8',
|
||||
size: 5,
|
||||
},
|
||||
interpolation: {
|
||||
type: 'smooth',
|
||||
},
|
||||
},
|
||||
],
|
||||
axes: {
|
||||
x: {
|
||||
type: 'category',
|
||||
position: 'bottom',
|
||||
line: {
|
||||
enabled: true,
|
||||
stroke: '#c8b9a7',
|
||||
},
|
||||
tick: {
|
||||
enabled: false,
|
||||
},
|
||||
label: {
|
||||
color: '#1f2933',
|
||||
fontSize: 12,
|
||||
},
|
||||
crosshair: {
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
y: {
|
||||
type: 'number',
|
||||
position: 'left',
|
||||
title: {
|
||||
text: '数值',
|
||||
text: '',
|
||||
},
|
||||
label: {
|
||||
color: '#1f2933',
|
||||
fontSize: 12,
|
||||
formatter: ({ value }) =>
|
||||
isCost
|
||||
? formatWan(Number(value))
|
||||
: Number(value).toLocaleString('zh-CN', {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
}),
|
||||
},
|
||||
line: {
|
||||
enabled: false,
|
||||
},
|
||||
tick: {
|
||||
enabled: false,
|
||||
},
|
||||
gridLine: {
|
||||
enabled: true,
|
||||
style: [
|
||||
{
|
||||
stroke: '#e5d9ca',
|
||||
lineDash: [0],
|
||||
},
|
||||
],
|
||||
},
|
||||
crosshair: {
|
||||
snap: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
enabled: false,
|
||||
},
|
||||
}),
|
||||
[chartType],
|
||||
);
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
}, [metricKey, selectedMetric.label]);
|
||||
|
||||
return (
|
||||
<main className="app-shell">
|
||||
<section className="toolbar" aria-label="图表设置">
|
||||
<div>
|
||||
<h1>AG Chart Service</h1>
|
||||
<p>React 单页图表服务,无路由。</p>
|
||||
</div>
|
||||
<div className="segmented-control" aria-label="切换图表类型">
|
||||
<button
|
||||
className={chartType === 'bar' ? 'active' : ''}
|
||||
type="button"
|
||||
onClick={() => setChartType('bar')}
|
||||
>
|
||||
柱状
|
||||
</button>
|
||||
<button
|
||||
className={chartType === 'line' ? 'active' : ''}
|
||||
type="button"
|
||||
onClick={() => setChartType('line')}
|
||||
>
|
||||
折线
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<main className="dashboard-shell">
|
||||
<div className="watermark-layer" aria-hidden="true">
|
||||
{Array.from({ length: 18 }).map((_, index) => (
|
||||
<span key={index}>众为数字化管理平台</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<section className="chart-panel" aria-label="业务趋势图表">
|
||||
<AgCharts options={chartOptions} />
|
||||
<section className="workspace" aria-label="年度费用模板" ref={workspaceRef}>
|
||||
<section className="chart-area" aria-label="年度总费用图表">
|
||||
<div className="chart-frame" ref={chartFrameRef}>
|
||||
<div className="metric-switcher">
|
||||
<button
|
||||
className="metric-switcher-button"
|
||||
type="button"
|
||||
title="切换纵坐标指标"
|
||||
aria-expanded={metricMenuOpen}
|
||||
aria-haspopup="menu"
|
||||
onClick={() => setMetricMenuOpen((open) => !open)}
|
||||
>
|
||||
{selectedMetric.label}
|
||||
</button>
|
||||
{metricMenuOpen ? (
|
||||
<div className="metric-switcher-menu" role="menu" aria-label="切换纵坐标指标">
|
||||
{metricOptions.map((option) => (
|
||||
<button
|
||||
className="metric-switcher-menu-item"
|
||||
type="button"
|
||||
role="menuitem"
|
||||
key={option.key}
|
||||
aria-current={option.key === metricKey}
|
||||
onClick={() => {
|
||||
setMetricKey(option.key);
|
||||
setMetricMenuOpen(false);
|
||||
}}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button className="chart-fullscreen-button ag-charts-toolbar__button" type="button" title="全屏(F11)">
|
||||
<i className="anticon anticon-arrow-salt ag-charts-myButton-fullScreen ag-charts-diy-button" />
|
||||
</button>
|
||||
<AgCharts options={chartOptions} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside className="right-placeholder" aria-label="右侧预留区域" />
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
|
||||
+406
-68
@@ -1,9 +1,8 @@
|
||||
:root {
|
||||
color: #172033;
|
||||
background: #f4f7fb;
|
||||
color: #161616;
|
||||
background: #f5e8d8;
|
||||
font-family:
|
||||
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
sans-serif;
|
||||
"Microsoft YaHei", "PingFang SC", "Segoe UI", Arial, sans-serif;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
@@ -15,102 +14,441 @@
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.app-shell {
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(360px, 1fr);
|
||||
gap: 18px;
|
||||
width: min(1180px, calc(100vw - 32px));
|
||||
min-height: 100vh;
|
||||
margin: 0 auto;
|
||||
padding: 24px 0;
|
||||
.wea-right-menu-show {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
.wea-right-menu,
|
||||
.wea-right-menu-show {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.dashboard-shell {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
background: #f5e8d8;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.watermark-layer {
|
||||
position: absolute;
|
||||
inset: -30px;
|
||||
z-index: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(260px, 1fr));
|
||||
grid-auto-rows: 128px;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
color: rgba(150, 140, 128, 0.2);
|
||||
font-size: 16px;
|
||||
letter-spacing: 0;
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
|
||||
.watermark-layer span {
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.workspace {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(540px, 52vw) 1fr;
|
||||
gap: 28px;
|
||||
height: 100vh;
|
||||
padding: 8px 28px 18px 64px;
|
||||
}
|
||||
|
||||
.chart-area {
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.toolbar h1 {
|
||||
.chart-title h1 {
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
line-height: 1.15;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.toolbar p {
|
||||
margin: 6px 0 0;
|
||||
color: #5b6578;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.segmented-control {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(2, minmax(72px, 1fr));
|
||||
min-width: 160px;
|
||||
padding: 4px;
|
||||
border: 1px solid #cfd7e6;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.segmented-control button {
|
||||
min-height: 34px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
color: #4a5568;
|
||||
background: transparent;
|
||||
.chart-title button {
|
||||
display: grid;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(95, 88, 80, 0.18);
|
||||
border-radius: 2px;
|
||||
color: #6d6258;
|
||||
background: rgba(255, 249, 241, 0.36);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 120ms ease,
|
||||
border-color 120ms ease,
|
||||
color 120ms ease,
|
||||
transform 120ms ease,
|
||||
box-shadow 120ms ease;
|
||||
}
|
||||
|
||||
.segmented-control button.active {
|
||||
color: #ffffff;
|
||||
background: #1f4ed8;
|
||||
.chart-title button:hover {
|
||||
border-color: rgba(0, 120, 168, 0.36);
|
||||
color: #0078a8;
|
||||
background: rgba(255, 252, 248, 0.94);
|
||||
box-shadow: 0 1px 5px rgba(69, 54, 36, 0.12);
|
||||
}
|
||||
|
||||
.chart-panel {
|
||||
min-height: 520px;
|
||||
padding: 16px;
|
||||
border: 1px solid #d9e1ee;
|
||||
border-radius: 8px;
|
||||
background: #ffffff;
|
||||
.chart-title button:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.chart-panel > div {
|
||||
.chart-title button:focus-visible {
|
||||
outline: 2px solid rgba(0, 120, 168, 0.34);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.chart-title button span {
|
||||
position: relative;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.chart-title button span::before {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 1px;
|
||||
width: 10px;
|
||||
height: 1px;
|
||||
background: currentColor;
|
||||
transform: rotate(-35deg);
|
||||
content: "";
|
||||
}
|
||||
|
||||
.chart-title button span::after {
|
||||
position: absolute;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
width: 4px;
|
||||
height: 1px;
|
||||
background: currentColor;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.chart-frame {
|
||||
position: relative;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
padding: 26px 0 0;
|
||||
}
|
||||
|
||||
.workspace:fullscreen {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
display: grid;
|
||||
grid-template-columns: minmax(540px, 52vw) 1fr;
|
||||
gap: 28px;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
padding: 8px 28px 18px 64px;
|
||||
background: #f5e8d8;
|
||||
}
|
||||
|
||||
.workspace:fullscreen .chart-area {
|
||||
height: calc(100vh - 26px);
|
||||
}
|
||||
|
||||
.workspace:fullscreen .right-placeholder {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.app-shell {
|
||||
width: min(100vw - 20px, 1180px);
|
||||
grid-template-rows: auto minmax(420px, 1fr);
|
||||
padding: 16px 0;
|
||||
.chart-frame > div {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-wrapper {
|
||||
--ag-charts-accent-color: #0078a8;
|
||||
--ag-charts-button-background-color: rgba(255, 249, 241, 0.72);
|
||||
--ag-charts-button-border-radius: 3px;
|
||||
--ag-charts-border-color: rgba(90, 82, 72, 0.22);
|
||||
--ag-charts-chrome-background-color: rgba(255, 249, 241, 0.84);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar {
|
||||
gap: 8px;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-annotations__toolbar {
|
||||
left: -46px;
|
||||
top: 24px;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-annotations__toolbar .ag-charts-toolbar {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
.metric-switcher {
|
||||
position: absolute;
|
||||
left: 74px;
|
||||
top: 272px;
|
||||
z-index: 12;
|
||||
}
|
||||
|
||||
.metric-switcher-button {
|
||||
display: block;
|
||||
width: 22px;
|
||||
min-height: 112px;
|
||||
padding: 8px 2px;
|
||||
border: 1px solid rgba(90, 82, 72, 0.16);
|
||||
border-radius: 3px;
|
||||
color: #111827;
|
||||
background: rgba(255, 241, 229, 0.68);
|
||||
font-size: 12px;
|
||||
letter-spacing: 2px;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background-color 120ms ease,
|
||||
border-color 120ms ease,
|
||||
color 120ms ease,
|
||||
box-shadow 120ms ease;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
}
|
||||
|
||||
.metric-switcher-button:hover,
|
||||
.metric-switcher-button[aria-expanded="true"] {
|
||||
color: #0078a8;
|
||||
border-color: rgba(0, 120, 168, 0.36);
|
||||
background: rgba(255, 252, 248, 0.94);
|
||||
box-shadow: 0 1px 5px rgba(69, 54, 36, 0.12);
|
||||
}
|
||||
|
||||
.metric-switcher-menu {
|
||||
position: absolute;
|
||||
left: 30px;
|
||||
top: 8px;
|
||||
width: max-content;
|
||||
min-width: 196px;
|
||||
padding: 4px 0;
|
||||
border: 1px solid rgba(90, 82, 72, 0.22);
|
||||
border-radius: 3px;
|
||||
background: #fbede1;
|
||||
box-shadow: 0 4px 14px rgba(69, 54, 36, 0.14);
|
||||
}
|
||||
|
||||
.metric-switcher-menu-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
border: 0;
|
||||
color: #262a33;
|
||||
background: transparent;
|
||||
font-size: 13px;
|
||||
line-height: 30px;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.metric-switcher-menu-item:hover,
|
||||
.metric-switcher-menu-item[aria-current="true"] {
|
||||
color: #0078a8;
|
||||
background: rgba(255, 252, 248, 0.94);
|
||||
}
|
||||
|
||||
.chart-frame .chart-fullscreen-button {
|
||||
position: absolute;
|
||||
left: 24px;
|
||||
top: 210px;
|
||||
z-index: 9;
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
min-width: 34px;
|
||||
min-height: 34px;
|
||||
place-items: center;
|
||||
padding: 0;
|
||||
border: 1px solid rgba(90, 82, 72, 0.22);
|
||||
border-radius: 3px;
|
||||
color: #46413b;
|
||||
background: rgba(255, 249, 241, 0.72);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button[title="Clear annotations"] {
|
||||
transform: translateY(42px);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-zoom-buttons {
|
||||
top: auto !important;
|
||||
right: 24px !important;
|
||||
bottom: 34px !important;
|
||||
left: auto !important;
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
z-index: 8;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transform: translateY(4px);
|
||||
transition:
|
||||
opacity 140ms ease,
|
||||
transform 140ms ease;
|
||||
}
|
||||
|
||||
.chart-frame:hover .ag-charts-zoom-buttons,
|
||||
.chart-frame:focus-within .ag-charts-zoom-buttons,
|
||||
.chart-frame .ag-charts-zoom-buttons:focus-within {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-zoom-buttons .ag-charts-toolbar {
|
||||
position: relative !important;
|
||||
z-index: 9;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
color: #46413b;
|
||||
border-color: rgba(90, 82, 72, 0.22);
|
||||
background: rgba(255, 249, 241, 0.72);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-zoom-buttons .ag-charts-toolbar__button {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
min-width: 26px;
|
||||
min-height: 26px;
|
||||
padding: 3px;
|
||||
background: rgba(255, 249, 241, 0.72);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button:hover,
|
||||
.chart-frame .ag-charts-toolbar__button--active {
|
||||
color: #0078a8;
|
||||
border-color: rgba(0, 120, 168, 0.36);
|
||||
background: rgba(255, 252, 248, 0.94);
|
||||
box-shadow: 0 1px 5px rgba(69, 54, 36, 0.12);
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-myButton-fullScreen {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-myButton-fullScreen::before,
|
||||
.chart-frame .ag-charts-myButton-fullScreen::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.chart-frame .anticon-arrow-salt::before {
|
||||
border-top: 1px solid currentColor;
|
||||
border-right: 1px solid currentColor;
|
||||
clip-path: polygon(54% 0, 100% 0, 100% 46%, 86% 46%, 86% 14%, 54% 14%);
|
||||
}
|
||||
|
||||
.chart-frame .anticon-arrow-salt::after {
|
||||
border-bottom: 1px solid currentColor;
|
||||
border-left: 1px solid currentColor;
|
||||
clip-path: polygon(0 54%, 14% 54%, 14% 86%, 46% 86%, 46% 100%, 0 100%);
|
||||
}
|
||||
|
||||
.chart-frame .anticon-shrink::before {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-right: 1px solid currentColor;
|
||||
border-bottom: 1px solid currentColor;
|
||||
}
|
||||
|
||||
.chart-frame .anticon-shrink::after {
|
||||
right: 2px;
|
||||
bottom: 2px;
|
||||
left: auto;
|
||||
top: auto;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-top: 1px solid currentColor;
|
||||
border-left: 1px solid currentColor;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.38;
|
||||
}
|
||||
|
||||
.chart-frame .ag-charts-toolbar__button[aria-disabled="true"] {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.38;
|
||||
}
|
||||
|
||||
.right-placeholder {
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
.workspace {
|
||||
grid-template-columns: 1fr;
|
||||
height: auto;
|
||||
min-height: 100vh;
|
||||
padding: 8px 18px 18px 56px;
|
||||
}
|
||||
|
||||
.toolbar h1 {
|
||||
font-size: 24px;
|
||||
.chart-area {
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.segmented-control {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chart-panel {
|
||||
min-height: 460px;
|
||||
padding: 10px;
|
||||
.right-placeholder {
|
||||
min-height: 280px;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user