Skip to content

Commit

Permalink
Merge pull request #114 from oceanbase/dengfuping-dev
Browse files Browse the repository at this point in the history
feat(charts): Support for theme
  • Loading branch information
dengfuping authored Sep 4, 2023
2 parents f3f115a + d967d35 commit 51d0193
Show file tree
Hide file tree
Showing 34 changed files with 735 additions and 286 deletions.
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';
Expand Down Expand Up @@ -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>
);
Expand Down
7 changes: 6 additions & 1 deletion .dumi/theme/layouts/GlobalLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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]);
}
});

Expand All @@ -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();

Expand Down
33 changes: 25 additions & 8 deletions docs/charts/charts-theme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,33 @@ 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(themeConfig.defaultColor);
// 折线图线宽
console.log(themeConfig.styleSheet.lineBorder);
// 设置主题
return (
<>
<ChartProvider theme="light">
{...}
</ChartProvider>
<ChartProvider theme="dark">
{...}
</ChartProvider>
<ChartProvider theme={{ defaultColor: '#ff0000', subColor: '#00ff00' }}>
{...}
</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
Expand Up @@ -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',
Expand All @@ -30,7 +36,7 @@ const Area: React.FC<AreaConfig> = forwardRef(
line: {
...line,
style: {
lineWidth: theme.styleSheet.lineBorder,
lineWidth: themeConfig.styleSheet.lineBorder,
...line?.style,
},
},
Expand All @@ -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,
},
Expand Down Expand Up @@ -81,7 +87,7 @@ const Area: React.FC<AreaConfig> = forwardRef(
type: 'brush-x',
},
],
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return (
Expand Down
1 change: 0 additions & 1 deletion packages/charts/src/Bar/demo/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export default () => {
position: 'top-left',
},
};
console.log(Bar);
return (
<Row gutter={200}>
<Col span={12}>
Expand Down
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
Expand All @@ -11,6 +13,7 @@ export interface BarConfig extends AntBarConfig {
warningPercent?: number;
// 危险水位线,仅 isProgress 为 true 时生效
dangerPercent?: number;
theme?: Theme;
}

const Bar: React.FC<BarConfig> = forwardRef(
Expand All @@ -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];
Expand All @@ -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,
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -112,7 +117,7 @@ const Bar: React.FC<BarConfig> = forwardRef(
? {
...barBackground,
style: {
fill: theme.barBackgroundColor,
fill: themeConfig.barBackgroundColor,
...barBackground?.style,
},
}
Expand All @@ -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,
},
Expand All @@ -142,7 +147,7 @@ const Bar: React.FC<BarConfig> = forwardRef(
...legend?.marker,
},
},
theme: 'ob',
theme: themeConfig.theme,
...restConfig,
};
return <AntBar {...newConfig} ref={ref} />;
Expand Down
64 changes: 64 additions & 0 deletions packages/charts/src/ChartProvider/__tests__/theme.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ChartProvider, useTheme } from '@oceanbase/charts';

describe('ChartProvider theme', () => {
it('default theme', () => {
const Child = () => {
const themeConfig = useTheme();
expect(themeConfig.theme).toBe('light');
expect(themeConfig.defaultColor).toBe('#4C96FF');
return <div />;
};
render(
<ChartProvider>
<Child />
</ChartProvider>
);
});

it('light theme', () => {
const Child = () => {
const themeConfig = useTheme();
expect(themeConfig.theme).toBe('light');
expect(themeConfig.defaultColor).toBe('#4C96FF');
return <div />;
};
render(
<ChartProvider theme="light">
<Child />
</ChartProvider>
);
});

it('dark theme', () => {
const Child = () => {
const themeConfig = useTheme();
expect(themeConfig.theme).toBe('dark');
expect(themeConfig.defaultColor).toBe('#4D97FF');
return <div />;
};
render(
<ChartProvider theme="dark">
<Child />
</ChartProvider>
);
});

it('custom theme config', () => {
const Child = () => {
const themeConfig = useTheme();
expect(themeConfig.theme).toBe('custom-theme');
expect(themeConfig.defaultColor).toBe('#ff0000');
expect(themeConfig.subColor).toBe('#00ff00');
return <div />;
};
render(
<ChartProvider
theme={{ theme: 'custom-theme', defaultColor: '#ff0000', subColor: '#00ff00' }}
>
<Child />
</ChartProvider>
);
});
});
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 = 'light', ...restProps }) => {
return (
<ChartContext.Provider
value={{
theme,
...restProps,
}}
>
{children}
</ChartContext.Provider>
);
};

ChartProvider.ChartContext = ChartContext;

export default ChartProvider;
Loading

0 comments on commit 51d0193

Please sign in to comment.