8000 feat: painting bar/column color based on categories by jwlee1108 · Pull Request #705 · nhn/tui.chart · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: painting bar/column color based on categories #705

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

Merged
merged 8 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion apps/chart/src/charts/barChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import { BarChartProps, SelectSeriesInfo } from '@t/charts';
* @param {Array<string>} props.data.categories - Categories.
* @param {Array<Object>} props.data.series - Series data.
* @param {string} props.data.series.name - Series name.
* @param {string} props.data.series.colorByCategories - Paint Rect with color based on categories.
* @param {Array<number|Array<number>>} props.data.series.data - Series data.
* @param {Object} [props.options] - Options for making Bar Chart.
* @param {Object} [props.options.chart]
Expand All @@ -63,7 +64,6 @@ import { BarChartProps, SelectSeriesInfo } from '@t/charts';
* @param {boolean} [props.options.series.selectable=false] - Whether to make selectable series or not.
* @param {number} [props.options.series.barWidth] - Bar width.
* @param {boolean} [props.options.series.diverging] - Whether to use diverging chart or not.
* @param {boolean} [props.options.series.colorByPoint] - Whether to use color feature or not.
* @param {Object} [props.options.series.stack] - Option to use the stack chart or, if so, what type of stack to use.
* @param {string} [props.options.series.eventDetectType] - Event detect type. 'grouped', 'point' is available.
* @param {Object} [props.options.series.dataLabels] - Set the visibility, location, and formatting of dataLabel. For specific information, refer to the {@link https://github.com/nhn/tui.chart|DataLabels guide} on github.
Expand Down
2 changes: 1 addition & 1 deletion apps/chart/src/charts/columnChart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { ColumnChartProps, SelectSeriesInfo } from '@t/charts';
* @param {Array<string>} props.data.categories - Categories.
* @param {Array<Object>} props.data.series - Series data.
* @param {string} props.data.series.name - Series name.
* @param {string} props.data.series.colorByCategories - Paint Rect with color based on categories.
* @param {Array<number|Array<number>>} props.data.series.data - Series data.
* @param {Object} [props.options] - Options for making Column Chart.
* @param {Object} [props.options.chart]
Expand All @@ -62,7 +63,6 @@ import { ColumnChartProps, SelectSeriesInfo } from '@t/charts';
* @param {boolean} [props.options.series.selectable=false] - Whether to make selectable series or not.
* @param {number} [props.options.series.barWidth] - Bar width.
* @param {boolean} [props.options.series.diverging] - Whether to use diverging chart or not.
* @param {boolean} [props.options.series.colorByPoint] - Whether to use color feature or not.
* @param {Object} [props.options.series.stack] - Option to use the stack chart or, if so, what type of stack to use.
* @param {string} [props.options.series.eventDetectType] - Event detect type. 'grouped', 'point' is available.
* @param {Object} [props.options.series.dataLabels] - Set the visibility, location, and formatting of dataLabel. For specific information, refer to the {@link https://github.com/nhn/tui.chart|DataLabels guide} on github.
Expand Down
12 changes: 8 additions & 4 deletions apps/chart/src/component/boxSeries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,16 +425,20 @@ export default class BoxSeries extends Component {
const seriesModels: RectModel[] = [];
const padding = (tickDistance - columnWidth * (validDiverging ? 1 : seriesLength)) / 2;

seriesData.forEach(({ data, color: seriesColor, name }, seriesIndex) => {
seriesData.forEach(({ data, color: seriesColor, name, colorByCategories }, seriesIndex) => {
const seriesPos = (diverging ? 0 : seriesIndex) * columnWidth + padding;
const isLBSideWithDiverging = diverging && isLeftBottomSide(seriesIndex);
const colorLength = colorByCategories ? seriesColor.length : 1;

this.isRangeData = isRangeData(data);

data.forEach((value, index) => {
const dataStart = seriesPos + index * tickDistance;
const barLength = this.makeBarLength(value, renderOptions);
const color = this.getSeriesColor(name, seriesColor);
const color = this.getSeriesColor(
name,
colorByCategories ? seriesColor[index % colorLength] : (seriesColor as string)
);

if (isNumber(barLength)) {
const startPosition = this.getStartPosition(
Expand Down Expand Up @@ -534,15 +538,15 @@ export default class BoxSeries extends Component {
): TooltipData[] {
const tooltipData: TooltipData[] = [];

seriesData.forEach(({ data, name, color }) => {
seriesData.forEach(({ data, name, color, colorByCategories }) => {
data.forEach((value, dataIndex) => {
if (!isNull(value)) {
const barLength = this.makeBarLength(value, renderOptions);

if (isNumber(barLength)) {
tooltipData.push({
label: name,
color,
color: colorByCategories ? color[dataIndex] : (color as string),
value: this.getTooltipValue(value),
category: categories.length ? categories[dataIndex] : '',
});
Expand Down
10 changes: 6 additions & 4 deletions apps/chart/src/component/boxStackSeries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,11 @@ export default class BoxStackSeries extends BoxSeries {
renderOptions,
isLBSideWithDiverging
);
const { name } = seriesRawData[seriesIndex];
const { name, colorByCategories, color: rawColor } = seriesRawData[seriesIndex];
const active = this.activeSeriesMap![name];
const color = getRGBA(seriesRawData[seriesIndex].color, active ? 1 : 0.2);
const colorLength = rawColor.length || 1;
const hexColor = colorByCategories ? rawColor[dataIndex % colorLength] : rawColor as string;
const color = getRGBA(hexColor, active ? 1 : 0.2);

seriesModels.push({
type: 'rect',
Expand Down Expand Up @@ -378,7 +380,7 @@ export default class BoxStackSeries extends BoxSeries {
): TooltipData[] {
const seriesRawData = seriesData.data;
const { stackData } = seriesData;
const colors = seriesRawData.map(({ color }) => color);
const colors = seriesRawData.map(({ color }) => color) as string[];
Copy link
5D40

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에도 colorstring 타입이란 가정하에 동작했던 건가요? 혹시 몰라서 여쭤봅니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네. 각 시리즈에 color값이 묶이는 구조인데 이번엔 컬러값이 배열로 들어와야 해서, 변수를 추가할까 타입을 추가할까 하다가 타입을 추가했어요.


return isGroupStack(stackData)
? this.makeGroupStackTooltipData(seriesRawData, stackData, categories)
Expand All @@ -394,7 +396,7 @@ export default class BoxStackSeries extends BoxSeries {
const rawDataWithSameGroupId = seriesRawData.filter(
({ stackGroup }) => stackGroup === groupId
);
const colors = rawDataWithSameGroupId.map(({ color }) => color);
const colors = rawDataWithSameGroupId.map(({ color }) => color) as string[];

return this.makeStackTooltipData(
rawDataWithSameGroupId,
Expand Down
1 change: 0 additions & 1 deletion apps/chart/src/helpers/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,6 @@ function getBarColumnSeriesTheme(globalFontFamily: string) {
const defaultDataLabelTheme = makeDefaultDataLabelsTheme(globalFontFamily);

return {
colorByPoint: false,
areaOpacity: 1,
hover: {
...boxDefault.BOX_HOVER,
Expand Down
83 changes: 57 additions & 26 deletions apps/chart/src/store/legend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
Legend,
CircleLegend,
LegendDataList,
Categories,
DefaultCategories,
} from '@t/store/store';
import {
BubbleChartOptions,
Expand Down Expand Up @@ -50,6 +52,8 @@ type LegendLabelsInfo = {
checked: boolean;
viewLabel: string;
width: number;
colorByCategories?: boolean;
colorIndex?: number;
}[];

type LegendInfo = {
Expand Down Expand Up @@ -298,23 +302,37 @@ function getViewLabelInfo(legendInfo: LegendInfo, label: string, maxTextLength?:
return { viewLabel, width: itemWidth ?? itemWidthWithFullText };
}

function getLegendLabelsInfo(series: RawSeries, legendInfo: LegendInfo): LegendLabelsInfo {
function getLegendLabelsInfo(
series: RawSeries,
legendInfo: LegendInfo,
categories: DefaultCategories
): LegendLabelsInfo {
const maxTextLengthWithEllipsis = getMaxTextLengthWithEllipsis(legendInfo);
let colorIndex = 0;

return Object.keys(series).flatMap((type) =>
series[type].map(({ name, colorValue, visible }) => {
return Object.keys(series).flatMap((type) => {
const labelInfo = series[type].map(({ name, colorValue, visible, colorByCategories }) => {
const label = colorValue ? colorValue : name;
const currentColorIndex = colorIndex;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이정도면 위에 선언된 변수가 그냥 currentColorIndex 여도 되지 않을까요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중간에 값이 변경되니까 따로 할당한 것 같습니다ㅎㅎ

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 이게 중간에 값이 변경되어야 해서 애매했어요. 레전드 입장에선 컬러 리스트만 알고 있는 상황이고, 시리즈 단위로 컬러를 N개 가져가는 구조가 되다보니 다음 컬러 인덱스를 잡아줄 변수가 필요했습니다.

const { width, viewLabel } = getViewLabelInfo(legendInfo, label, maxTextLengthWithEllipsis);

colorIndex += colorByCategories ? categories.length : 1;

return {
label,
type,
colorByCategories: !!colorByCategories,
colorIndex: currentColorIndex,
checked: visible ?? true,
viewLabel,
width,
};
})
);
});

colorIndex += series[type].length - 1;

return labelInfo;
});
}

function getItemWidth(
Expand All @@ -337,14 +355,20 @@ function getLegendDataAppliedTheme(data: LegendDataList, series: Series) {
(acc, cur) => (cur && cur.colors ? [...acc, ...cur.colors] : acc),
[]
);
const hasColorByCategories = data.some((legend) => legend.colorByCategories);

return data.map((datum, idx) => ({
...datum,
color: colors[idx],
}));
return data.map((datum, idx) => {
const { colorByCategories, colorIndex } = datum;
const index = hasColorByCategories ? colorIndex || idx : idx;
Comment on lines +361 to +362
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const { colorByCategories, colorIndex } = datum;
const index = hasColorByCategories ? colorIndex || idx : idx;
const { colorByCategories, colorIndex = idx } = datum;
const index = hasColorByCategories ? colorIndex : idx;

는 안될까요?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

colorIndex || idx이면 0인 경우에도 변경되는 건데 의도된 동작인거죠~?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0인 경우에도 문제가 없으리라 판단했는데 이 부분은 조금 더 확인해보겠습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lja1018 colorByCateogories가 없을 땐 기존대로 가야해서 조금 어려울 것 같아요. 확인 후 가능하면 코드를 좀 더 정리해보겠습니다.


return {
...datum,
color: colorByCategories ? '#aaa' : colors[index % colors.length],
};
});
}

function getLegendState(options: Options, series: RawSeries): Legend {
function getLegendState(options: Options, series: RawSeries, categories?: Categories): Legend {
const useSpectrumLegend =
(options?.series as TreemapChartSeriesOptions)?.useColorValue ?? !!series.heatmap;

Expand All @@ -365,19 +389,23 @@ function getLegendState(options: Options, series: RawSeries): Legend {

const legendLabelsInfo = hasNestedPieSeries(series)
? getNestedPieLegendLabelsInfo(series, legendInfo)
: getLegendLabelsInfo(series, legendInfo);

const data = legendLabelsInfo.map(({ label, type, checked, width, viewLabel }) => ({
label,
active: true,
checked,
width,
iconType: getIconType(type),
chartType: type,
rowIndex: 0,
columnIndex: 0,
viewLabel,
}));
: getLegendLabelsInfo(series, legendInfo, categories as DefaultCategories);

const data = legendLabelsInfo.map(
({ label, type, checked, width, viewLabel, colorByCategories, colorIndex }) => ({
label,
active: true,
checked,
width,
iconType: getIconType(type),
chartType: type,
rowIndex: 0,
columnIndex: 0,
viewLabel,
colorByCategories,
colorIndex,
})
);

return {
useSpectrumLegend,
Expand Down Expand Up @@ -445,15 +473,18 @@ function setIndexToLegendData(

const legend: StoreModule = {
name: 'legend',
state: ({ options, series }) => {
state: ({ options, series, categories }) => {
return {
legend: getLegendState(options, series) as Legend,
legend: getLegendState(options, series, categories) as Legend,
circleLegend: {} as CircleLegend,
};
},
action: {
initLegendState({ state, initStoreState }) {
extend(state.legend, getLegendState(initStoreState.options, initStoreState.series));
extend(
state.legend,
getLegendState(initStoreState.options, initStoreState.series, initStoreState.categories)
);
},
setLegendLayout({ state }) {
if (state.legend.useSpectrumLegend) {
Expand Down
38 changes: 30 additions & 8 deletions apps/chart/src/store/seriesData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ function getCoordinateDataRange(data, rawCategories: string[], zoomRange: RangeD
return [start, end];
}

function getSeriesColors(
colors: string[],
colorIndex: number,
size: number,
isColorByCategories: boolean< F438 /span>
) {
return isColorByCategories ? colors.slice(0, size + 1) : colors[colorIndex % colors.length];
}

function getSeriesDataInRange(
data,
rawCategories: Categories,
Expand Down Expand Up @@ -173,19 +182,32 @@ const seriesData: StoreModule = {
const rawSeries = deepCopy(initStoreState.series);
const { disabledSeries, theme, zoomRange, rawCategories } = state;
const newSeriesData = {};
let colorIndex = 0;

Object.keys(rawSeries).forEach((seriesName) => {
const { colors, iconTypes } = theme.series![seriesName];
let originSeriesData = rawSeries[seriesName].map((m, idx) => ({
...m,
rawData: m.data,
data: getSeriesDataInRange(m.data, rawCategories, seriesName, zoomRange),
color: colors ? colors[idx % colors.length] : '',
}));

let originSeriesData = rawSeries[seriesName].map((series) => {
const isColorByCategories = !!series.colorByCategories;
const size = isColorByCategories ? (rawCategories as string[]).length : 1;

const color = colors
? getSeriesColors(colors, colorIndex, size, isColorByCategories)
: '';

colorIndex += size;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 연산 이후로 colorIndex를 쓰는 곳이 없어보이는데 연산하는 이유가 있나요~?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loop로 돌면서 195라인에서 계속 데이터를 사용하고 있어요. 이 함수 밖에서 사용 중입니다. reduce로 할까 했는데, 제 생각엔 map 성격이 더 강해보여서 함수 외부에 변수를 두고 누적 계산하는 식으로 처리했습니다.


return {
...series,
rawData: series.data,
data: getSeriesDataInRange(series.data, rawCategories, seriesName, zoomRange),
color,
};
});

if (seriesName === 'scatter') {
originSeriesData = originSeriesData.map((m, idx) => ({
...m,
originSeriesData = originSeriesData.map((series, idx) => ({
...series,
iconType: iconTypes ? iconTypes[idx] : 'circle',
}));
}
Expand Down
Loading
0