Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(charts): Support for theme #114

Merged
merged 2 commits into from
Sep 4, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(charts): Support for theme
dengfuping committed Sep 4, 2023
commit c445fbf5c38510472d6e6a017205a2fcf7d8dffd
3 changes: 2 additions & 1 deletion .dumi/theme/SiteThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ConfigProvider, token } from '@oceanbase/design';
import { ChartProvider } from '@oceanbase/charts';
import type { ThemeProviderProps } from 'antd-style';
import { ThemeProvider } from 'antd-style';
import type { FC } from 'react';
@@ -40,7 +41,7 @@ const SiteThemeProvider: FC<ThemeProviderProps> = ({ children, theme, ...rest })
direction={direction}
locale={lang === 'cn' ? zhCN : undefined}
>
{children}
<ChartProvider theme={theme.isDark ? 'dark' : 'light'}>{children}</ChartProvider>
</ConfigProvider>
</ThemeProvider>
);
7 changes: 6 additions & 1 deletion .dumi/theme/layouts/GlobalLayout.tsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@ import {
} from '@ant-design/cssinjs';
import { App, theme as obTheme } from '@oceanbase/design';
import type { DirectionType } from '@oceanbase/design/es/config-provider';
import { createSearchParams, useOutlet, useSearchParams } from 'dumi';
import { usePrefersColor, createSearchParams, useOutlet, useSearchParams } from 'dumi';
import React, { useCallback, useEffect, useMemo } from 'react';
import useLayoutState from '../../hooks/useLayoutState';
import SiteThemeProvider from '../SiteThemeProvider';
@@ -42,6 +42,7 @@ const GlobalLayout: React.FC = () => {
const outlet = useOutlet();
const { pathname } = useLocation();
const [searchParams, setSearchParams] = useSearchParams();
const [, , setPrefersColor] = usePrefersColor();
const [{ theme = [], direction, isMobile }, setSiteState] = useLayoutState<SiteState>({
isMobile: false,
direction: 'ltr',
@@ -69,6 +70,8 @@ const GlobalLayout: React.FC = () => {
...nextSearchParams,
theme: value.filter(t => t !== 'light'),
});
// Set theme of dumi site
setPrefersColor(value?.filter(t => t === 'dark' || t === 'light')?.[0]);
}
});

@@ -88,6 +91,8 @@ const GlobalLayout: React.FC = () => {
const _direction = searchParams.get('direction') as DirectionType;

setSiteState({ theme: _theme, direction: _direction === 'rtl' ? 'rtl' : 'ltr' });
// Set theme of dumi site
setPrefersColor(_theme?.filter(t => t === 'dark' || t === 'light')?.[0]);
// Handle isMobile
updateMobileMode();

23 changes: 15 additions & 8 deletions docs/charts/charts-theme.md
Original file line number Diff line number Diff line change
@@ -6,16 +6,23 @@ group: 可视化图表

OceanBase Charts 的设计体系遵循 AntV 设计规范,并在此基础上扩展出了很多具备 OceanBase 产品风格的设计规范模式,包括但不限于全局样式(色板、圆角、边框)和特定图表的视觉定制,以传递 OceanBase 科技、活力、专注和关怀的品牌特点。

## 使用主题 Token
## 使用主题配置

```ts
import { theme } from '@oceanbase/charts';
```tsx | pure
import { ChartProvider, useTheme } from '@oceanbase/charts';

// 主题色
console.log(theme.defaultColor);

// 折线图线宽
console.log(theme.styleSheet.lineBorder);
export default () {
const themeConfig = useTheme();
// 主题色
console.log(theme.defaultColor);
// 折线图线宽
console.log(theme.styleSheet.lineBorder);
return (
<ChartProvider theme="dark">
{...}
</ChartProvider>
);
};
```

- 主题的全量 Token 可参考 https://github.com/oceanbase/charts/blob/master/src/theme/index.ts#L29 。
18 changes: 12 additions & 6 deletions packages/charts/src/Area/index.tsx
Original file line number Diff line number Diff line change
@@ -5,14 +5,20 @@ import { sortByMoment } from '@oceanbase/util';
import useResizeObserver from 'use-resize-observer';
import type { Tooltip } from '../hooks/useTooltipScrollable';
import useTooltipScrollable from '../hooks/useTooltipScrollable';
import { theme } from '../theme';
import { useTheme } from '../theme';
import type { Theme } from '../theme';

export interface AreaConfig extends AntAreaConfig {
tooltip?: false | Tooltip;
theme?: Theme;
}

const Area: React.FC<AreaConfig> = forwardRef(
({ data, line, xField, xAxis, yAxis, tooltip, legend, interactions, ...restConfig }, ref) => {
(
{ data, line, xField, xAxis, yAxis, tooltip, legend, interactions, theme, ...restConfig },
ref
) => {
const themeConfig = useTheme(theme);
const { ref: containerRef, height: containerHeight } = useResizeObserver<HTMLDivElement>({
// 包含 padding 和 border
box: 'border-box',
@@ -30,7 +36,7 @@ const Area: React.FC<AreaConfig> = forwardRef(
line: {
...line,
style: {
lineWidth: theme.styleSheet.lineBorder,
lineWidth: themeConfig.styleSheet.lineBorder,
...line?.style,
},
},
@@ -50,8 +56,8 @@ const Area: React.FC<AreaConfig> = forwardRef(
line: {
...xAxis?.grid?.line,
style: {
lineWidth: theme.styleSheet.axisGridBorder,
stroke: theme.styleSheet.axisGridBorderColor,
lineWidth: themeConfig.styleSheet.axisGridBorder,
stroke: themeConfig.styleSheet.axisGridBorderColor,
lineDash: [4, 4],
...xAxis?.grid?.line?.style,
},
@@ -81,7 +87,7 @@ const Area: React.FC<AreaConfig> = forwardRef(
type: 'brush-x',
},
],
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return (
1 change: 0 additions & 1 deletion packages/charts/src/Bar/demo/basic.tsx
Original file line number Diff line number Diff line change
@@ -31,7 +31,6 @@ export default () => {
position: 'top-left',
},
};
console.log(Bar);
return (
<Row gutter={200}>
<Col span={12}>
25 changes: 15 additions & 10 deletions packages/charts/src/Bar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { forwardRef } from 'react';
import type { BarConfig as AntBarConfig } from '@ant-design/charts';
import { Bar as AntBar } from '@ant-design/charts';
import { theme } from '../theme';
import { uniq } from 'lodash';
import { toPercent } from '../util/number';
import { useTheme } from '../theme';
import type { Theme } from '../theme';

export interface BarConfig extends AntBarConfig {
// 是否为进度条形图,数值范围为 0 ~ 1
@@ -11,6 +13,7 @@ export interface BarConfig extends AntBarConfig {
warningPercent?: number;
// 危险水位线,仅 isProgress 为 true 时生效
dangerPercent?: number;
theme?: Theme;
}

const Bar: React.FC<BarConfig> = forwardRef(
@@ -32,14 +35,16 @@ const Bar: React.FC<BarConfig> = forwardRef(
xAxis,
yAxis,
legend,
theme,
...restConfig
},
ref
) => {
const themeConfig = useTheme(theme);
const stackValues =
(isStack &&
seriesField &&
data?.filter(item => item[seriesField]).map(item => item[seriesField])) ||
uniq(data?.filter(item => item[seriesField]).map(item => item[seriesField]))) ||
[];
// 堆叠柱状图中最后一段对应的值
const lastStackValue = stackValues?.[stackValues?.length - 1];
@@ -51,8 +56,8 @@ const Bar: React.FC<BarConfig> = forwardRef(
isPercent,
isRange,
seriesField,
maxBarWidth: theme.barWidth,
minBarWidth: theme.barWidth,
maxBarWidth: themeConfig.barWidth,
minBarWidth: themeConfig.barWidth,
meta: isProgress
? {
...meta,
@@ -78,9 +83,9 @@ const Bar: React.FC<BarConfig> = forwardRef(
let color;
if (isProgress) {
if (dangerPercent && datum[xField] >= dangerPercent) {
color = theme.semanticRed;
color = themeConfig.semanticRed;
} else if (warningPercent && datum[xField] >= warningPercent) {
color = theme.semanticYellow;
color = themeConfig.semanticYellow;
}
}

@@ -112,7 +117,7 @@ const Bar: React.FC<BarConfig> = forwardRef(
? {
...barBackground,
style: {
fill: theme.barBackgroundColor,
fill: themeConfig.barBackgroundColor,
...barBackground?.style,
},
}
@@ -125,8 +130,8 @@ const Bar: React.FC<BarConfig> = forwardRef(
line: {
...yAxis?.grid?.line,
style: {
lineWidth: theme.styleSheet.axisGridBorder,
stroke: theme.styleSheet.axisGridBorderColor,
lineWidth: themeConfig.styleSheet.axisGridBorder,
stroke: themeConfig.styleSheet.axisGridBorderColor,
lineDash: [4, 4],
...yAxis?.grid?.line?.style,
},
@@ -142,7 +147,7 @@ const Bar: React.FC<BarConfig> = forwardRef(
...legend?.marker,
},
},
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return <AntBar {...newConfig} ref={ref} />;
11 changes: 11 additions & 0 deletions packages/charts/src/ChartProvider/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

export type Theme = 'light' | 'dark' | string | object;

export interface ChartConsumerProps {
theme?: Theme;
}

export const ChartContext = React.createContext<ChartConsumerProps>({
theme: 'light',
});
28 changes: 28 additions & 0 deletions packages/charts/src/ChartProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { ChartContext } from './context';
import type { ChartConsumerProps } from './context';

export * from './context';

export interface ChartProviderProps extends ChartConsumerProps {
children?: React.ReactNode;
}

const ChartProvider: React.FC<ChartProviderProps> & {
ChartContext: typeof ChartContext;
} = ({ children, theme, ...restProps }) => {
return (
<ChartContext.Provider
value={{
theme,
...restProps,
}}
>
{children}
</ChartContext.Provider>
);
};

ChartProvider.ChartContext = ChartContext;

export default ChartProvider;
26 changes: 17 additions & 9 deletions packages/charts/src/Column/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import React, { forwardRef } from 'react';
import type { ColumnConfig as AntColumnConfig } from '@ant-design/charts';
import { Column as AntColumn } from '@ant-design/charts';
import { theme } from '../theme';
import { uniq } from 'lodash';
import { useTheme } from '../theme';
import type { Theme } from '../theme';

export type ColumnConfig = AntColumnConfig;
export interface ColumnConfig extends AntColumnConfig {
theme?: Theme;
}

const Column: React.FC<ColumnConfig> = forwardRef(
({ data, isStack, isGroup, isRange, seriesField, label, xAxis, legend, ...restConfig }, ref) => {
(
{ data, isStack, isGroup, isRange, seriesField, label, xAxis, legend, theme, ...restConfig },
ref
) => {
const themeConfig = useTheme(theme);
const stackValues =
(isStack &&
seriesField &&
data?.filter(item => item[seriesField]).map(item => item[seriesField])) ||
uniq(data?.filter(item => item[seriesField]).map(item => item[seriesField]))) ||
[];
// 堆叠柱状图中最后一段对应的值
const lastStackValue = stackValues?.[0];
@@ -20,8 +28,8 @@ const Column: React.FC<ColumnConfig> = forwardRef(
isGroup,
isRange,
seriesField,
maxColumnWidth: theme.columnWidth,
minColumnWidth: theme.columnWidth,
maxColumnWidth: themeConfig.columnWidth,
minColumnWidth: themeConfig.columnWidth,
// 普通柱状图 label 会展示在顶部,需要留出一定空间,否则 label 会被遮挡
appendPadding: isStack || isGroup || isRange ? 0 : [16, 0, 0, 0],
// 分组柱状图组内柱子间距,仅分组柱状图生效
@@ -62,8 +70,8 @@ const Column: React.FC<ColumnConfig> = forwardRef(
line: {
...xAxis?.grid?.line,
style: {
lineWidth: theme.styleSheet.axisGridBorder,
stroke: theme.styleSheet.axisGridBorderColor,
lineWidth: themeConfig.styleSheet.axisGridBorder,
stroke: themeConfig.styleSheet.axisGridBorderColor,
lineDash: [4, 4],
...xAxis?.grid?.line?.style,
},
@@ -79,7 +87,7 @@ const Column: React.FC<ColumnConfig> = forwardRef(
...legend?.marker,
},
},
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return <AntColumn {...newConfig} ref={ref} />;
18 changes: 11 additions & 7 deletions packages/charts/src/DualAxes/index.tsx
Original file line number Diff line number Diff line change
@@ -7,19 +7,23 @@ import { sortByMoment } from '@oceanbase/util';
import useResizeObserver from 'use-resize-observer';
import type { Tooltip } from '../hooks/useTooltipScrollable';
import useTooltipScrollable from '../hooks/useTooltipScrollable';
import { theme } from '../theme';
import { useTheme } from '../theme';
import type { Theme } from '../theme';

export interface DualAxesConfig extends Omit<AntDualAxesConfig, 'yAxis'> {
// 限制双轴图的 yAxis 为对象格式,而非数组格式。官方文档的示例均为对象格式,方便统一用法
yAxis: false | Record<string, Axis>;
tooltip?: false | Tooltip;
theme?: Theme;
}

const DualAxes: React.FC<DualAxesConfig> = forwardRef(
(
{ data, xField, yField, xAxis, yAxis, tooltip, legend, geometryOptions, ...restConfig },
{ data, xField, yField, xAxis, yAxis, tooltip, legend, geometryOptions, theme, ...restConfig },
ref
) => {
const themeConfig = useTheme(theme);

const yField1 = yField?.[0];
const yField2 = yField?.[1];

@@ -59,8 +63,8 @@ const DualAxes: React.FC<DualAxesConfig> = forwardRef(
line: {
...xAxis?.grid?.line,
style: {
lineWidth: theme.styleSheet.axisGridBorder,
stroke: theme.styleSheet.axisGridBorderColor,
lineWidth: themeConfig.styleSheet.axisGridBorder,
stroke: themeConfig.styleSheet.axisGridBorderColor,
lineDash: [4, 4],
...xAxis?.grid?.line?.style,
},
@@ -106,8 +110,8 @@ const DualAxes: React.FC<DualAxesConfig> = forwardRef(
// 堆叠柱状图中最后一段对应的值
const lastStackValue = stackValues?.[0];
defaultGeometryOption = {
maxColumnWidth: theme.columnWidth,
minColumnWidth: theme.columnWidth,
maxColumnWidth: themeConfig.columnWidth,
minColumnWidth: themeConfig.columnWidth,
columnStyle: datum => {
return {
radius:
@@ -130,7 +134,7 @@ const DualAxes: React.FC<DualAxesConfig> = forwardRef(
...item,
};
}),
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return (
Loading