From 0d5bc4948a865c8ac4d5583a2ff93da8e2f8e74f Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 27 Sep 2024 16:54:42 -0400 Subject: [PATCH 1/2] feat(charts): import charts from @patternfly/react-charts/victory --- packages/react-charts/victory/package.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/react-charts/victory/package.json diff --git a/packages/react-charts/victory/package.json b/packages/react-charts/victory/package.json new file mode 100644 index 00000000000..1be7f7b05c5 --- /dev/null +++ b/packages/react-charts/victory/package.json @@ -0,0 +1 @@ +{"name":"@patternfly/react-charts-victory","main":"../dist/js/victory/index.js","module":"../dist/esm/victory/index.js","typings":"../dist/esm/victory/index.d.ts","version":"8.0.0-prerelease.12","private":true} From b998f8dae59251c35ee327bad6bace786ec276a8 Mon Sep 17 00:00:00 2001 From: Dan Labrecque Date: Fri, 6 Sep 2024 07:54:22 -0400 Subject: [PATCH 2/2] feat(charts): sankey --- packages/react-charts/package.json | 6 +- .../react-charts/single-packages.config.json | 2 +- .../echarts/components/Sankey/Sankey.test.tsx | 83 ++++ .../src/echarts/components/Sankey/Sankey.tsx | 210 +++++++++ .../components/Sankey/examples/Basic.tsx | 96 ++++ .../components/Sankey/examples/Sankey.md | 22 + .../src/echarts/components/Sankey/index.ts | 1 + .../src/echarts/components/Sankey/theme.ts | 415 ++++++++++++++++++ .../src/echarts/components/index.ts | 1 + .../src/echarts/components/utils/misc.ts | 23 + .../src/echarts/components/utils/observe.ts | 38 ++ .../src/echarts/components/utils/theme.ts | 9 + packages/react-charts/src/echarts/index.ts | 1 + packages/react-charts/subpaths.config.json | 2 +- packages/react-charts/tsconfig.json | 7 +- packages/react-charts/victory/package.json | 2 +- packages/react-docs/package.json | 1 + yarn.lock | 38 +- 18 files changed, 947 insertions(+), 10 deletions(-) create mode 100644 packages/react-charts/src/echarts/components/Sankey/Sankey.test.tsx create mode 100644 packages/react-charts/src/echarts/components/Sankey/Sankey.tsx create mode 100644 packages/react-charts/src/echarts/components/Sankey/examples/Basic.tsx create mode 100644 packages/react-charts/src/echarts/components/Sankey/examples/Sankey.md create mode 100644 packages/react-charts/src/echarts/components/Sankey/index.ts create mode 100644 packages/react-charts/src/echarts/components/Sankey/theme.ts create mode 100644 packages/react-charts/src/echarts/components/index.ts create mode 100644 packages/react-charts/src/echarts/components/utils/misc.ts create mode 100644 packages/react-charts/src/echarts/components/utils/observe.ts create mode 100644 packages/react-charts/src/echarts/components/utils/theme.ts create mode 100644 packages/react-charts/src/echarts/index.ts diff --git a/packages/react-charts/package.json b/packages/react-charts/package.json index 2b8018b615b..3b76bf5b653 100644 --- a/packages/react-charts/package.json +++ b/packages/react-charts/package.json @@ -7,6 +7,9 @@ "types": "dist/esm/index.d.ts", "typesVersions": { "*": { + "echarts": [ + "dist/esm/echarts/index.d.ts" + ], "victory": [ "dist/esm/victory/index.d.ts" ] @@ -43,6 +46,7 @@ "tslib": "^2.7.0" }, "peerDependencies": { + "echarts": "^5.5.1", "react": "^17 || ^18", "react-dom": "^17 || ^18", "victory-area": "^37.1.1", @@ -69,7 +73,7 @@ "subpaths": "node ../../scripts/exportSubpaths.mjs --config subpaths.config.json" }, "devDependencies": { - "@types/lodash": "^4.17.9", + "@types/lodash": "^4.17.7", "fs-extra": "^11.2.0" } } diff --git a/packages/react-charts/single-packages.config.json b/packages/react-charts/single-packages.config.json index 4a9651bb17d..8b418fd2095 100644 --- a/packages/react-charts/single-packages.config.json +++ b/packages/react-charts/single-packages.config.json @@ -1,4 +1,4 @@ { "packageName": "@patternfly/react-charts", - "exclude": ["dist/esm/deprecated/index.js", "dist/esm/next/index.js"] + "exclude": ["dist/esm/deprecated/index.js"] } diff --git a/packages/react-charts/src/echarts/components/Sankey/Sankey.test.tsx b/packages/react-charts/src/echarts/components/Sankey/Sankey.test.tsx new file mode 100644 index 00000000000..ff3418f6d94 --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/Sankey.test.tsx @@ -0,0 +1,83 @@ +import * as React from 'react'; +// import * as echarts from 'echarts'; +import { render } from '@testing-library/react'; +import { Sankey } from './Sankey'; + +const data = [ + { + name: 'a' + }, + { + name: 'b' + }, + { + name: 'a1' + }, + { + name: 'a2' + }, + { + name: 'b1' + }, + { + name: 'c' + } +]; + +const links = [ + { + source: 'a', + target: 'a1', + value: 5 + }, + { + source: 'a', + target: 'a2', + value: 3 + }, + { + source: 'b', + target: 'b1', + value: 8 + }, + { + source: 'a', + target: 'b1', + value: 3 + }, + { + source: 'b1', + target: 'a1', + value: 1 + }, + { + source: 'b1', + target: 'c', + value: 2 + } +]; + +let spy: any; + +// beforeAll(() => { +// console.log(`*** TEST 1`); +// spy = jest.spyOn(echarts, 'getInstanceByDom').mockImplementation( +// () => +// ({ +// hideLoading: jest.fn(), +// setOption: jest.fn(), +// showLoading: jest.fn() +// }) as any +// ); +// }); +// +// afterAll(() => { +// console.log(`*** TEST 2`); +// spy.mockRestore(); +// }); + +// See https://stackoverflow.com/questions/54921743/testing-echarts-react-component-with-jest-echartelement-is-null +xtest('renders component data', () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-charts/src/echarts/components/Sankey/Sankey.tsx b/packages/react-charts/src/echarts/components/Sankey/Sankey.tsx new file mode 100644 index 00000000000..0ec68e11a66 --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/Sankey.tsx @@ -0,0 +1,210 @@ +/* eslint-disable camelcase */ +import chart_voronoi_flyout_stroke_Fill from '@patternfly/react-tokens/dist/esm/chart_voronoi_flyout_stroke_Fill'; +import chart_voronoi_labels_Fill from '@patternfly/react-tokens/dist/esm/chart_voronoi_labels_Fill'; + +import * as React from 'react'; +import * as echarts from 'echarts'; +import { useCallback, useRef, useState } from 'react'; +import defaultsDeep from 'lodash/defaultsDeep'; +import { getMutationObserver } from '../utils/observe'; +import { getComputedStyle } from '../utils/theme'; + +// import { BarChart, SankeyChart } from 'echarts/charts'; +// import { CanvasRenderer } from 'echarts/renderers'; + +// import { +// TitleComponent, +// TooltipComponent, +// GridComponent, +// DatasetComponent, +// TransformComponent +// } from 'echarts/components'; + +// Register the required components +// echarts.use([ +// BarChart, +// SankeyChart, +// TitleComponent, +// TooltipComponent, +// GridComponent, +// DatasetComponent, +// TransformComponent, +// LabelLayout, +// UniversalTransition, +// CanvasRenderer +// ]); + +import { getTheme } from './theme'; +import { getClassName } from '../utils/misc'; + +/** + */ +export interface SankeyProps { + className?: string; + destinationLabel?: string; + height?: number; + id?: string; + legend?: { + symbolSize?: number; // Todo: move into tooltip? + }; + lineStyle?: any; + + /** + * This creates a Mutation Observer to watch the given DOM selector. + * + * When the pf-v6-theme-dark selector is added or removed, this component will be notified to update its computed + * theme styles. However, if the dark theme is not updated dynamically (e.g., via a toggle), there is no need to add + * this Mutation Observer. + * + * Note: Don't provide ".pf-v6-theme-dark" as the node selector as it won't exist in the page for light theme. + * The underlying querySelectorAll() function needs to find the element the dark theme selector will be added to. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Locating_DOM_elements_using_selectors + * + * @propType string + * @example + * @example + * @example + */ + nodeSelector?: string; + opts?: any; + series: any[]; + sourceLabel?: string; + theme?: any; + title?: any; + tooltip?: any; + width?: number; +} + +export const Sankey: React.FunctionComponent = ({ + className, + destinationLabel = 'Destination', + height, + id, + legend = { + symbolSize: 10 + }, + lineStyle = { + color: 'source', + opacity: 0.6 + }, + nodeSelector, + opts, + series, + sourceLabel = 'Source', + theme, + title, + tooltip = { + valueFormatter: (value: number | string) => value + }, + width +}: SankeyProps) => { + const containerRef = useRef(); + const echart = useRef(); + const [chartTheme, setChartTheme] = useState(theme || getTheme()); + + const getItemColor = useCallback( + (params: any) => { + const serie = series[params.seriesIndex]; + const sourceData = serie?.data.find((datum: any) => datum.name === params.data?.source); + const targetData = serie?.data.find((datum: any) => datum.name === params.data?.target); + const sourceColor = sourceData?.itemStyle?.color; + const targetColor = targetData?.itemStyle?.color; + return { sourceColor, targetColor }; + }, + [series] + ); + + const getTooltip = useCallback(() => { + const symbolSize = `${legend.symbolSize}px`; + const defaults = { + backgroundColor: getComputedStyle(chart_voronoi_flyout_stroke_Fill), + confine: true, + formatter: (params: any) => { + const result = ` +
+ ${params.name} ${params.value} + `; + if (params.data.source && params.data.target) { + const { sourceColor, targetColor } = getItemColor(params); + return ` +

${sourceLabel}

+
+ ${params.data.source} +

${destinationLabel}

+

+

+ ${params.data.target} + + ${tooltip.valueFormatter(params.value, params.dataIndex)} + +

+ `; + } + return result.replace(/\s\s+/g, ' '); + }, + textStyle: { + color: getComputedStyle(chart_voronoi_labels_Fill) + }, + trigger: 'item', + triggerOn: 'mousemove' + }; + return defaultsDeep(tooltip, defaults); + }, [destinationLabel, getItemColor, legend.symbolSize, sourceLabel, tooltip]); + + React.useEffect(() => { + echarts.registerTheme('pf-v5-sankey', chartTheme); + echart.current = echarts.init(containerRef.current, 'pf-v5-sankey', { renderer: 'svg' }); // renderer: 'svg' + + const newSeries = series.map((serie: any) => { + const defaults = { + data: serie.data.map((datum: any, index: number) => ({ + itemStyle: { + color: chartTheme?.color[index % chartTheme?.color.length] + } + })), + emphasis: { + focus: 'adjacency' + }, + layout: 'none', + lineStyle, + type: 'sankey' + }; + return defaultsDeep(serie, defaults); + }); + + echart.current?.setOption({ + series: newSeries, + title, + tooltip: getTooltip() + }); + + return () => { + echart.current?.dispose(); + }; + }, [chartTheme, containerRef, getTooltip, lineStyle, opts, series, title, tooltip]); + + // Resize observer + React.useEffect(() => { + echart.current?.resize(); + }, [height, width]); + + // Dark theme observer + React.useEffect(() => { + let observer = () => {}; + observer = getMutationObserver(nodeSelector, () => { + setChartTheme(getTheme()); + }); + return () => { + observer(); + }; + }, [nodeSelector]); + + const getSize = () => ({ + ...(height && { height: `${height}px` }), + ...(width && { width: `${width}px` }) + }); + + return
; +}; +Sankey.displayName = 'Sankey'; diff --git a/packages/react-charts/src/echarts/components/Sankey/examples/Basic.tsx b/packages/react-charts/src/echarts/components/Sankey/examples/Basic.tsx new file mode 100644 index 00000000000..93737ce1028 --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/examples/Basic.tsx @@ -0,0 +1,96 @@ +import React from 'react'; +import { Sankey } from '@patternfly/react-charts/echarts'; +import { getResizeObserver } from '@patternfly/react-core'; + +export const FormBasic: React.FunctionComponent = () => { + const data = [ + { + name: 'a' + }, + { + name: 'b' + }, + { + name: 'a1' + }, + { + name: 'a2' + }, + { + name: 'b1' + }, + { + name: 'c' + } + ]; + + const links = [ + { + source: 'a', + target: 'a1', + value: 5 + }, + { + source: 'a', + target: 'a2', + value: 3 + }, + { + source: 'b', + target: 'b1', + value: 8 + }, + { + source: 'a', + target: 'b1', + value: 3 + }, + { + source: 'b1', + target: 'a1', + value: 1 + }, + { + source: 'b1', + target: 'c', + value: 2 + } + ]; + + // let observer = () => {}; + const containerRef = React.useRef(); + const [width, setWidth] = React.useState(0); + + React.useEffect(() => { + const handleResize = () => { + if (containerRef.current && containerRef.current.clientWidth) { + setWidth(containerRef.current.clientWidth); + } + }; + let observer = () => {}; + observer = getResizeObserver(containerRef.current, handleResize); + + return () => { + observer(); + }; + }, [containerRef, width]); + + return ( +
+ `${value} GiB` + }} + width={width} + /> +
+ ); +}; diff --git a/packages/react-charts/src/echarts/components/Sankey/examples/Sankey.md b/packages/react-charts/src/echarts/components/Sankey/examples/Sankey.md new file mode 100644 index 00000000000..7e8565e150a --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/examples/Sankey.md @@ -0,0 +1,22 @@ +--- +id: Sankey +section: charts +propComponents: [ + 'Sankey', +] +beta: true +--- + +import { Sankey } from '@patternfly/react-charts/echarts'; + +## Introduction +Note: PatternFly React charts live in its own package at [@patternfly/react-charts](https://www.npmjs.com/package/@patternfly/react-charts)! + +PatternFly React charts are based on the [Apache ECharts](https://echarts.apache.org/) chart library, along with additional functionality, custom components, and theming for PatternFly. This provides a collection of React based components you can use to build PatternFly patterns with consistent markup, styling, and behavior. + +## Examples +### Basic + +```ts file="./Basic.tsx" + +``` diff --git a/packages/react-charts/src/echarts/components/Sankey/index.ts b/packages/react-charts/src/echarts/components/Sankey/index.ts new file mode 100644 index 00000000000..4bb44aaee6a --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/index.ts @@ -0,0 +1 @@ +export * from './Sankey'; diff --git a/packages/react-charts/src/echarts/components/Sankey/theme.ts b/packages/react-charts/src/echarts/components/Sankey/theme.ts new file mode 100644 index 00000000000..41fbab2f64c --- /dev/null +++ b/packages/react-charts/src/echarts/components/Sankey/theme.ts @@ -0,0 +1,415 @@ +/* eslint-disable camelcase */ +import chart_theme_multi_color_ordered_ColorScale_100 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_100'; +import chart_theme_multi_color_ordered_ColorScale_200 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_200'; +import chart_theme_multi_color_ordered_ColorScale_300 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_300'; +import chart_theme_multi_color_ordered_ColorScale_400 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_400'; +import chart_theme_multi_color_ordered_ColorScale_500 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_500'; +import chart_theme_multi_color_ordered_ColorScale_600 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_600'; +import chart_theme_multi_color_ordered_ColorScale_700 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_700'; +import chart_theme_multi_color_ordered_ColorScale_800 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_800'; +import chart_theme_multi_color_ordered_ColorScale_900 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_900'; +import chart_theme_multi_color_ordered_ColorScale_1000 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1000'; +import chart_theme_multi_color_ordered_ColorScale_1100 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1100'; +import chart_theme_multi_color_ordered_ColorScale_1200 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1200'; +import chart_theme_multi_color_ordered_ColorScale_1300 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1300'; +import chart_theme_multi_color_ordered_ColorScale_1400 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1400'; +import chart_theme_multi_color_ordered_ColorScale_1500 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1500'; +import chart_theme_multi_color_ordered_ColorScale_1600 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1600'; +import chart_theme_multi_color_ordered_ColorScale_1700 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1700'; +import chart_theme_multi_color_ordered_ColorScale_1800 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1800'; +import chart_theme_multi_color_ordered_ColorScale_1900 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_1900'; +import chart_theme_multi_color_ordered_ColorScale_2000 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2000'; +import chart_theme_multi_color_ordered_ColorScale_2100 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2100'; +import chart_theme_multi_color_ordered_ColorScale_2200 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2200'; +import chart_theme_multi_color_ordered_ColorScale_2300 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2300'; +import chart_theme_multi_color_ordered_ColorScale_2400 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2400'; +import chart_theme_multi_color_ordered_ColorScale_2500 from '@patternfly/react-tokens/dist/esm/chart_theme_multi_color_ordered_ColorScale_2500'; +import chart_global_label_Fill from '@patternfly/react-tokens/dist/esm/chart_global_label_Fill'; + +import { getComputedStyle } from '../utils/theme'; + +export const getTheme = () => { + // The color order below improves the color contrast in unordered charts; area & line + // See https://github.com/patternfly/patternfly-next/issues/1551 + const COLOR_SCALE = [ + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_100), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_200), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_300), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_400), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_500), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_600), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_700), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_800), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_900), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1000), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1100), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1200), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1300), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1400), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1500), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1600), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1700), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1800), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_1900), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2000), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2100), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2200), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2300), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2400), + getComputedStyle(chart_theme_multi_color_ordered_ColorScale_2500) + ]; + + return { + color: COLOR_SCALE, + backgroundColor: 'rgba(0,0,0,0)', + label: { + color: getComputedStyle(chart_global_label_Fill) + }, + textStyle: {}, + title: { + textStyle: { + // color: '#464646' + color: getComputedStyle(chart_global_label_Fill) + }, + subtextStyle: { + // color: '#6e7079' + color: getComputedStyle(chart_global_label_Fill) + } + }, + line: { + itemStyle: { + borderWidth: 1 + }, + lineStyle: { + width: 2 + }, + symbolSize: 4, + symbol: 'emptyCircle', + smooth: false + }, + radar: { + itemStyle: { + borderWidth: 1 + }, + lineStyle: { + width: 2 + }, + symbolSize: 4, + symbol: 'emptyCircle', + smooth: false + }, + bar: { + itemStyle: { + barBorderWidth: 0, + barBorderColor: '#ccc' + } + }, + pie: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + scatter: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + boxplot: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + parallel: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + sankey: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + funnel: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + gauge: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + } + }, + candlestick: { + itemStyle: { + color: '#eb5454', + color0: '#47b262', + borderColor: '#eb5454', + borderColor0: '#47b262', + borderWidth: 1 + } + }, + graph: { + itemStyle: { + borderWidth: 0, + borderColor: '#ccc' + }, + lineStyle: { + width: 1, + color: '#aaaaaa' + }, + symbolSize: 4, + symbol: 'emptyCircle', + smooth: false, + color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + label: { + color: '#eeeeee' + } + }, + map: { + itemStyle: { + areaColor: '#eee', + borderColor: '#444', + borderWidth: 0.5 + }, + label: { + color: '#000' + }, + emphasis: { + itemStyle: { + areaColor: 'rgba(255,215,0,0.8)', + borderColor: '#444', + borderWidth: 1 + }, + label: { + color: 'rgb(100,0,0)' + } + } + }, + geo: { + itemStyle: { + areaColor: '#eee', + borderColor: '#444', + borderWidth: 0.5 + }, + label: { + color: '#000' + }, + emphasis: { + itemStyle: { + areaColor: 'rgba(255,215,0,0.8)', + borderColor: '#444', + borderWidth: 1 + }, + label: { + color: 'rgb(100,0,0)' + } + } + }, + categoryAxis: { + axisLine: { + show: true, + lineStyle: { + color: '#6E7079' + } + }, + axisTick: { + show: true, + lineStyle: { + color: '#6E7079' + } + }, + axisLabel: { + show: true, + color: '#6E7079' + }, + splitLine: { + show: false, + lineStyle: { + color: ['#E0E6F1'] + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] + } + } + }, + valueAxis: { + axisLine: { + show: false, + lineStyle: { + color: '#6E7079' + } + }, + axisTick: { + show: false, + lineStyle: { + color: '#6E7079' + } + }, + axisLabel: { + show: true, + color: '#6E7079' + }, + splitLine: { + show: true, + lineStyle: { + color: ['#E0E6F1'] + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] + } + } + }, + logAxis: { + axisLine: { + show: false, + lineStyle: { + color: '#6E7079' + } + }, + axisTick: { + show: false, + lineStyle: { + color: '#6E7079' + } + }, + axisLabel: { + show: true, + color: '#6E7079' + }, + splitLine: { + show: true, + lineStyle: { + color: ['#E0E6F1'] + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] + } + } + }, + timeAxis: { + axisLine: { + show: true, + lineStyle: { + color: '#6E7079' + } + }, + axisTick: { + show: true, + lineStyle: { + color: '#6E7079' + } + }, + axisLabel: { + show: true, + color: '#6E7079' + }, + splitLine: { + show: false, + lineStyle: { + color: ['#E0E6F1'] + } + }, + splitArea: { + show: false, + areaStyle: { + color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] + } + } + }, + toolbox: { + iconStyle: { + borderColor: '#999999' + }, + emphasis: { + iconStyle: { + borderColor: '#666666' + } + } + }, + legend: { + textStyle: { + color: '#333333' + } + }, + tooltip: { + axisPointer: { + lineStyle: { + color: '#cccccc', + width: 1 + }, + crossStyle: { + color: '#cccccc', + width: 1 + } + } + }, + timeline: { + lineStyle: { + color: '#dae1f5', + width: 2 + }, + itemStyle: { + color: '#a4b1d7', + borderWidth: 1 + }, + controlStyle: { + color: '#a4b1d7', + borderColor: '#a4b1d7', + borderWidth: 1 + }, + checkpointStyle: { + color: '#316bf3', + borderColor: '#ffffff' + }, + label: { + color: '#a4b1d7' + }, + emphasis: { + itemStyle: { + color: '#ffffff' + }, + controlStyle: { + color: '#a4b1d7', + borderColor: '#a4b1d7', + borderWidth: 1 + }, + label: { + color: '#a4b1d7' + } + } + }, + visualMap: { + color: ['#bf444c', '#d88273', '#f6efa6'] + }, + dataZoom: { + handleSize: 'undefined%', + textStyle: {} + }, + markPoint: { + label: { + color: '#eeeeee' + }, + emphasis: { + label: { + color: '#eeeeee' + } + } + } + }; +}; diff --git a/packages/react-charts/src/echarts/components/index.ts b/packages/react-charts/src/echarts/components/index.ts new file mode 100644 index 00000000000..4bb44aaee6a --- /dev/null +++ b/packages/react-charts/src/echarts/components/index.ts @@ -0,0 +1 @@ +export * from './Sankey'; diff --git a/packages/react-charts/src/echarts/components/utils/misc.ts b/packages/react-charts/src/echarts/components/utils/misc.ts new file mode 100644 index 00000000000..ea6255e57f9 --- /dev/null +++ b/packages/react-charts/src/echarts/components/utils/misc.ts @@ -0,0 +1,23 @@ +/** + * Copied from exenv + * @private + */ +export const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement); + +/** + * Returns the class name that will be applied to the outermost div rendered by the chart's container + * @private + */ +export const getClassName = (className: string) => { + let cleanClassName; + + // Cleanup class name + if (className) { + cleanClassName = className + .replace(/pf-v6-c-chart/g, '') + .replace(/pf-c-chart/g, '') + .replace(/\s+/g, ' ') + .trim(); + } + return cleanClassName?.length ? `pf-v6-c-chart ${cleanClassName}` : 'pf-v6-c-chart'; +}; diff --git a/packages/react-charts/src/echarts/components/utils/observe.ts b/packages/react-charts/src/echarts/components/utils/observe.ts new file mode 100644 index 00000000000..364efdba387 --- /dev/null +++ b/packages/react-charts/src/echarts/components/utils/observe.ts @@ -0,0 +1,38 @@ +/** + * Mutation Observer Helper function + * //developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe + * + * @param {string} selector The DOM selector to watch + * @param {object} opt MutationObserver options + * @param {function} cb Pass Mutation object to a callback function + * @private + */ +export const observe = (selector: any, opt: any, cb: any) => { + let unobserve: any; + + if (selector) { + const Obs = new MutationObserver((m) => [...m].forEach(cb)); + document.querySelectorAll(selector).forEach((el) => Obs.observe(el, opt)); + unobserve = () => Obs.disconnect(); + } + return () => { + if (unobserve) { + unobserve(); + } + }; +}; + +// See https://stackoverflow.com/questions/17134823/detect-element-style-changes-with-javascript +export const getMutationObserver = (nodeSelector: string, cb: any) => + observe( + nodeSelector, + { + attributesList: ['style'], // Only the "style" attribute + attributeOldValue: true // Report also the oldValue + }, + (m: any) => { + if (cb) { + cb(m); + } + } + ); diff --git a/packages/react-charts/src/echarts/components/utils/theme.ts b/packages/react-charts/src/echarts/components/utils/theme.ts new file mode 100644 index 00000000000..e6dff369fc9 --- /dev/null +++ b/packages/react-charts/src/echarts/components/utils/theme.ts @@ -0,0 +1,9 @@ +import { canUseDOM } from '../utils/misc'; + +// See https://github.com/apache/echarts/issues/19743 +export const getComputedStyle = (token: any) => { + if (canUseDOM) { + return window.getComputedStyle(document.body).getPropertyValue(token.name); + } + return token.value; +}; diff --git a/packages/react-charts/src/echarts/index.ts b/packages/react-charts/src/echarts/index.ts new file mode 100644 index 00000000000..07635cbbc8e --- /dev/null +++ b/packages/react-charts/src/echarts/index.ts @@ -0,0 +1 @@ +export * from './components'; diff --git a/packages/react-charts/subpaths.config.json b/packages/react-charts/subpaths.config.json index 5c5e5d1baff..706c0581100 100644 --- a/packages/react-charts/subpaths.config.json +++ b/packages/react-charts/subpaths.config.json @@ -1,4 +1,4 @@ { "packageName": "@patternfly/react-charts", - "paths": ["victory"] + "paths": ["echarts", "victory"] } diff --git a/packages/react-charts/tsconfig.json b/packages/react-charts/tsconfig.json index 30bec2faa4a..cfd3101ef1b 100644 --- a/packages/react-charts/tsconfig.json +++ b/packages/react-charts/tsconfig.json @@ -1,9 +1,14 @@ { "extends": "../tsconfig.base.json", "compilerOptions": { + "jsx": "react", "rootDir": "./src", "outDir": "./dist/esm", - "tsBuildInfoFile": "dist/esm.tsbuildinfo" + "tsBuildInfoFile": "dist/esm.tsbuildinfo", + "baseUrl": ".", + "paths": { + "./next": ["./src/next"] + } }, "include": [ "./src/*", diff --git a/packages/react-charts/victory/package.json b/packages/react-charts/victory/package.json index 1be7f7b05c5..1e36a9be341 100644 --- a/packages/react-charts/victory/package.json +++ b/packages/react-charts/victory/package.json @@ -1 +1 @@ -{"name":"@patternfly/react-charts-victory","main":"../dist/js/victory/index.js","module":"../dist/esm/victory/index.js","typings":"../dist/esm/victory/index.d.ts","version":"8.0.0-prerelease.12","private":true} +{"name":"@patternfly/react-charts-victory","main":"../dist/js/victory/index.js","module":"../dist/esm/victory/index.js","typings":"../dist/esm/victory/index.d.ts","version":"8.0.0-prerelease.13","private":true} diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 42bf69df1b9..4acd330d4bf 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -33,6 +33,7 @@ "@patternfly/react-table": "workspace:^", "@patternfly/react-templates": "workspace:^", "@patternfly/react-tokens": "workspace:^", + "echarts": "^5.5.1", "victory": "^37.1.1" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 4d07318fdd8..266467a9f5f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3160,12 +3160,13 @@ __metadata: dependencies: "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" - "@types/lodash": "npm:^4.17.9" + "@types/lodash": "npm:^4.17.7" fs-extra: "npm:^11.2.0" hoist-non-react-statics: "npm:^3.3.2" lodash: "npm:^4.17.21" tslib: "npm:^2.7.0" peerDependencies: + echarts: ^5.5.1 react: ^17 || ^18 react-dom: ^17 || ^18 victory-area: ^37.1.1 @@ -3241,6 +3242,7 @@ __metadata: "@patternfly/react-table": "workspace:^" "@patternfly/react-templates": "workspace:^" "@patternfly/react-tokens": "workspace:^" + echarts: "npm:^5.5.1" victory: "npm:^37.1.1" languageName: unknown linkType: soft @@ -4457,10 +4459,10 @@ __metadata: languageName: node linkType: hard -"@types/lodash@npm:^4.17.9": - version: 4.17.9 - resolution: "@types/lodash@npm:4.17.9" - checksum: 10c0/54de935e835508b5f835a5dfaedd2b9a299685a21d11e9c5cd2dde57331d03bc2f98b71d2424ca8460f447ecd55a673e45ccdb70e58f9f72745710f6b91abc60 +"@types/lodash@npm:^4.17.7": + version: 4.17.7 + resolution: "@types/lodash@npm:4.17.7" + checksum: 10c0/40c965b5ffdcf7ff5c9105307ee08b782da228c01b5c0529122c554c64f6b7168fc8f11dc79aa7bae4e67e17efafaba685dc3a47e294dbf52a65ed2b67100561 languageName: node linkType: hard @@ -8703,6 +8705,16 @@ __metadata: languageName: node linkType: hard +"echarts@npm:^5.5.1": + version: 5.5.1 + resolution: "echarts@npm:5.5.1" + dependencies: + tslib: "npm:2.3.0" + zrender: "npm:5.6.0" + checksum: 10c0/2f7e3037f17fda99d977092767943f4d9b0c8f886f86701ec88591707713b5e5fd683e56086b6ba5245b322f088184bdb06eac488234c20a1869b08cb6b4e523 + languageName: node + linkType: hard + "editions@npm:^2.2.0": version: 2.3.1 resolution: "editions@npm:2.3.1" @@ -20038,6 +20050,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.3.0": + version: 2.3.0 + resolution: "tslib@npm:2.3.0" + checksum: 10c0/a845aed84e7e7dbb4c774582da60d7030ea39d67307250442d35c4c5dd77e4b44007098c37dd079e100029c76055f2a362734b8442ba828f8cc934f15ed9be61 + languageName: node + linkType: hard + "tslib@npm:^1.8.1, tslib@npm:^1.9.0": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -22172,3 +22191,12 @@ __metadata: checksum: 10c0/856117aa15cf5103d2a2fb173f0ab4acb12b4b4d0ed3ab249fdbbf612e55d1cadfd27a6110940e24746fb0a78cf640b522cc8bca76f30a3b00b66e90cf82abe0 languageName: node linkType: hard + +"zrender@npm:5.6.0": + version: 5.6.0 + resolution: "zrender@npm:5.6.0" + dependencies: + tslib: "npm:2.3.0" + checksum: 10c0/f7c5a1739dfec60b9bead0d0657c47868391b1009cc82a603f9dbf247fa625df28dcdb3e7b2e18404657e2c987f95e0e1bb5613519c2d823854f3dda44e2ee96 + languageName: node + linkType: hard