提交 469fd041 编写于 作者: P pissang

Merge branch 'master' into treeshakable-exports

...@@ -112,13 +112,13 @@ class CandlestickSeriesModel extends SeriesModel<CandlestickSeriesOption> { ...@@ -112,13 +112,13 @@ class CandlestickSeriesModel extends SeriesModel<CandlestickSeriesOption> {
clip: true, clip: true,
itemStyle: { itemStyle: {
color: '#c23531', // 阳线 positive color: '#eb5454', // positive
color0: '#314656', // 阴线 negative '#c23531', '#314656' color0: '#47b262', // negative
borderWidth: 1, borderColor: '#eb5454',
// FIXME borderColor0: '#47b262',
// ec2中使用的是lineStyle.color 和 lineStyle.color0 // borderColor: '#d24040',
borderColor: '#c23531', // borderColor0: '#398f4f',
borderColor0: '#314656' borderWidth: 1
}, },
emphasis: { emphasis: {
......
...@@ -17,8 +17,10 @@ ...@@ -17,8 +17,10 @@
* under the License. * under the License.
*/ */
import { DataTransformOption, ExternalDataTransform } from '../../data/helper/transform'; import {
import { DimensionIndex, OptionDataItem } from '../../util/types'; DataTransformOption, ExternalDataTransform, DataTransformDataItem, ExternalDataTransformResultItem
} from '../../data/helper/transform';
import { DimensionIndex } from '../../util/types';
import { parseConditionalExpression, ConditionalExpressionOption } from '../../util/conditionalExpression'; import { parseConditionalExpression, ConditionalExpressionOption } from '../../util/conditionalExpression';
import { hasOwn, createHashMap } from 'zrender/src/core/util'; import { hasOwn, createHashMap } from 'zrender/src/core/util';
import { makePrintable, throwError } from '../../util/log'; import { makePrintable, throwError } from '../../util/log';
...@@ -41,7 +43,7 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = { ...@@ -41,7 +43,7 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = {
// is better than return the entire raw soruce for user to find the mistake. // is better than return the entire raw soruce for user to find the mistake.
const upstream = params.upstream; const upstream = params.upstream;
let rawItem: OptionDataItem; let rawItem: DataTransformDataItem;
const condition = parseConditionalExpression<{ dimIdx: DimensionIndex }>(params.config, { const condition = parseConditionalExpression<{ dimIdx: DimensionIndex }>(params.config, {
...@@ -84,12 +86,12 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = { ...@@ -84,12 +86,12 @@ export const filterTransform: ExternalDataTransform<FilterTransformOption> = {
for (let i = 0, len = upstream.count(); i < len; i++) { for (let i = 0, len = upstream.count(); i < len; i++) {
rawItem = upstream.getRawDataItem(i); rawItem = upstream.getRawDataItem(i);
if (condition.evaluate()) { if (condition.evaluate()) {
resultData.push(rawItem); resultData.push(rawItem as any);
} }
} }
return { return {
data: resultData data: resultData as ExternalDataTransformResultItem['data']
}; };
} }
}; };
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
* under the License. * under the License.
*/ */
import { DataTransformOption, ExternalDataTransform } from '../../data/helper/transform'; import {
DataTransformOption, ExternalDataTransform, ExternalDataTransformResultItem
} from '../../data/helper/transform';
import { import {
DimensionLoose, DimensionIndex, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_OBJECT_ROWS DimensionLoose, DimensionIndex, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_OBJECT_ROWS
} from '../../util/types'; } from '../../util/types';
...@@ -199,7 +201,7 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = { ...@@ -199,7 +201,7 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
}); });
return { return {
data: resultData data: resultData as ExternalDataTransformResultItem['data']
}; };
} }
}; };
......
...@@ -221,7 +221,7 @@ class List< ...@@ -221,7 +221,7 @@ class List<
// We have an extra array store here. It's faster to be acessed than KV structured `_storage`. // We have an extra array store here. It's faster to be acessed than KV structured `_storage`.
// We profile the code `storage[dim]` and it seems to be KeyedLoadIC_Megamorphic instead of fast property access. // We profile the code `storage[dim]` and it seems to be KeyedLoadIC_Megamorphic instead of fast property access.
// Not sure why this happens. But using an extra array seems leads to faster `initData` // Not sure why this happens. But using an extra array seems leads to faster `initData`
// See https://github.com/apache/incubator-echarts/pull/13314 for more explaination. // See https://github.com/apache/incubator-echarts/pull/13314 for more explanation.
private _storageArr: DataValueChunk[] = []; private _storageArr: DataValueChunk[] = [];
private _nameList: string[] = []; private _nameList: string[] = [];
private _idList: string[] = []; private _idList: string[] = [];
......
...@@ -287,7 +287,10 @@ function makeEncodeDefine( ...@@ -287,7 +287,10 @@ function makeEncodeDefine(
: null; : null;
} }
function detectSourceFormat(data: DatasetOption['source']): SourceFormat { /**
* Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`.
*/
export function detectSourceFormat(data: DatasetOption['source']): SourceFormat {
let sourceFormat: SourceFormat = SOURCE_FORMAT_UNKNOWN; let sourceFormat: SourceFormat = SOURCE_FORMAT_UNKNOWN;
if (isTypedArray(data)) { if (isTypedArray(data)) {
...@@ -323,9 +326,6 @@ function detectSourceFormat(data: DatasetOption['source']): SourceFormat { ...@@ -323,9 +326,6 @@ function detectSourceFormat(data: DatasetOption['source']): SourceFormat {
} }
} }
} }
else if (data != null) {
throw new Error('Invalid data');
}
return sourceFormat; return sourceFormat;
} }
......
...@@ -82,31 +82,6 @@ export function resetSourceDefaulter(ecModel: GlobalModel): void { ...@@ -82,31 +82,6 @@ export function resetSourceDefaulter(ecModel: GlobalModel): void {
innerGlobalModel(ecModel).datasetMap = createHashMap(); innerGlobalModel(ecModel).datasetMap = createHashMap();
} }
// See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
export function inheritSourceMetaRawOption(
upstream: Source, // Can be null/undefined
newMetaRawOption: SourceMetaRawOption // Can NOT be null/undefined
): SourceMetaRawOption {
const parentMetaRawOption = upstream ? upstream.metaRawOption : null;
const seriesLayoutBy = retrieve2(
newMetaRawOption.seriesLayoutBy,
parentMetaRawOption ? parentMetaRawOption.seriesLayoutBy : null
);
// sourceHeader and dimensions should use the "detected result" rather than "meta raw".
// Consider the case: transform return only "data" but no "dimensions", that should means inherit
// dimensions definition from upstream. But the returned data does not contain header line and can not
// be used as dimension-detection. In this case we should use "detected dimensions" of upstream directly.
const sourceHeader = retrieve2(
newMetaRawOption.sourceHeader,
upstream ? upstream.startIndex : null
);
const dimensions = retrieve2(
newMetaRawOption.dimensions,
upstream ? upstream.dimensionsDefine : null
);
return { seriesLayoutBy, sourceHeader, dimensions };
}
/** /**
* [The strategy of the arrengment of data dimensions for dataset]: * [The strategy of the arrengment of data dimensions for dataset]:
* "value way": all axes are non-category axes. So series one by one take * "value way": all axes are non-category axes. So series one by one take
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
import { DatasetModel } from '../../component/dataset/install'; import { DatasetModel } from '../../component/dataset/install';
import SeriesModel from '../../model/Series'; import SeriesModel from '../../model/Series';
import { setAsPrimitive, map, isTypedArray, assert, each } from 'zrender/src/core/util'; import { setAsPrimitive, map, isTypedArray, assert, each, retrieve2 } from 'zrender/src/core/util';
import { SourceMetaRawOption, Source, createSource, cloneSourceShallow } from '../Source'; import { SourceMetaRawOption, Source, createSource, cloneSourceShallow } from '../Source';
import { import {
SeriesEncodableModel, OptionSourceData, SeriesEncodableModel, OptionSourceData,
...@@ -27,8 +27,7 @@ import { ...@@ -27,8 +27,7 @@ import {
SourceFormat, SeriesLayoutBy, OptionSourceHeader, DimensionDefinitionLoose SourceFormat, SeriesLayoutBy, OptionSourceHeader, DimensionDefinitionLoose
} from '../../util/types'; } from '../../util/types';
import { import {
querySeriesUpstreamDatasetModel, queryDatasetUpstreamDatasetModels, querySeriesUpstreamDatasetModel, queryDatasetUpstreamDatasetModels
inheritSourceMetaRawOption
} from './sourceHelper'; } from './sourceHelper';
import { applyDataTransform } from './transform'; import { applyDataTransform } from './transform';
...@@ -192,7 +191,7 @@ export class SourceManager { ...@@ -192,7 +191,7 @@ export class SourceManager {
const seriesModel = sourceHost as SeriesEncodableModel; const seriesModel = sourceHost as SeriesEncodableModel;
let data; let data;
let sourceFormat: SourceFormat; let sourceFormat: SourceFormat;
let upSource; let upSource: Source;
// Has upstream dataset // Has upstream dataset
if (hasUpstream) { if (hasUpstream) {
...@@ -212,14 +211,27 @@ export class SourceManager { ...@@ -212,14 +211,27 @@ export class SourceManager {
} }
// See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root. // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root.
const thisMetaRawOption = inheritSourceMetaRawOption( const newMetaRawOption = this._getSourceMetaRawOption();
upSource, const upMetaRawOption = upSource ? upSource.metaRawOption : null;
this._getSourceMetaRawOption() const seriesLayoutBy = retrieve2(
newMetaRawOption.seriesLayoutBy,
upMetaRawOption ? upMetaRawOption.seriesLayoutBy : null
);
const sourceHeader = retrieve2(
newMetaRawOption.sourceHeader,
upMetaRawOption ? upMetaRawOption.sourceHeader : null
);
// Note here we should not use `upSource.dimensionsDefine`. Consider the case:
// `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`,
// but series need `seriesLayoutBy: 'row'`.
const dimensions = retrieve2(
newMetaRawOption.dimensions,
upMetaRawOption ? upMetaRawOption.dimensions : null
); );
resultSourceList = [createSource( resultSourceList = [createSource(
data, data,
thisMetaRawOption, { seriesLayoutBy, sourceHeader, dimensions },
sourceFormat, sourceFormat,
seriesModel.get('encode', true) seriesModel.get('encode', true)
)]; )];
......
...@@ -18,26 +18,22 @@ ...@@ -18,26 +18,22 @@
*/ */
import { import {
Dictionary, OptionSourceData, DimensionDefinitionLoose, Dictionary, DimensionDefinitionLoose,
SourceFormat, DimensionDefinition, OptionDataItem, DimensionIndex, SourceFormat, DimensionDefinition, DimensionIndex,
OptionDataValue, DimensionLoose, DimensionName, ParsedValue, OptionDataValue, DimensionLoose, DimensionName, ParsedValue,
SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS, SERIES_LAYOUT_BY_COLUMN, SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ARRAY_ROWS,
OptionSourceDataObjectRows, OptionSourceDataArrayRows OptionSourceDataObjectRows, OptionSourceDataArrayRows
} from '../../util/types'; } from '../../util/types';
import { normalizeToArray } from '../../util/model'; import { normalizeToArray } from '../../util/model';
import { import {
createHashMap, bind, each, hasOwn, map, clone, isObject, createHashMap, bind, each, hasOwn, map, clone, isObject, extend
isArrayLike,
extend,
isArray
} from 'zrender/src/core/util'; } from 'zrender/src/core/util';
import { import {
getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter getRawSourceItemGetter, getRawSourceDataCounter, getRawSourceValueGetter
} from './dataProvider'; } from './dataProvider';
import { parseDataValue } from './dataValueHelper'; import { parseDataValue } from './dataValueHelper';
import { inheritSourceMetaRawOption } from './sourceHelper';
import { consoleLog, makePrintable, throwError } from '../../util/log'; import { consoleLog, makePrintable, throwError } from '../../util/log';
import { createSource, Source } from '../Source'; import { createSource, Source, SourceMetaRawOption, detectSourceFormat } from '../Source';
export type PipedDataTransformOption = DataTransformOption[]; export type PipedDataTransformOption = DataTransformOption[];
...@@ -71,7 +67,7 @@ export interface ExternalDataTransformResultItem { ...@@ -71,7 +67,7 @@ export interface ExternalDataTransformResultItem {
/** /**
* If `data` is null/undefined, inherit upstream data. * If `data` is null/undefined, inherit upstream data.
*/ */
data: OptionSourceData; data: OptionSourceDataArrayRows | OptionSourceDataObjectRows;
/** /**
* A `transform` can optionally return a dimensions definition. * A `transform` can optionally return a dimensions definition.
* The rule: * The rule:
...@@ -83,6 +79,7 @@ export interface ExternalDataTransformResultItem { ...@@ -83,6 +79,7 @@ export interface ExternalDataTransformResultItem {
*/ */
dimensions?: DimensionDefinitionLoose[]; dimensions?: DimensionDefinitionLoose[];
} }
export type DataTransformDataItem = ExternalDataTransformResultItem['data'][number];
export interface ExternalDimensionDefinition extends Partial<DimensionDefinition> { export interface ExternalDimensionDefinition extends Partial<DimensionDefinition> {
// Mandatory // Mandatory
index: DimensionIndex; index: DimensionIndex;
...@@ -110,7 +107,7 @@ export class ExternalSource { ...@@ -110,7 +107,7 @@ export class ExternalSource {
throw new Error('not supported'); throw new Error('not supported');
} }
getRawDataItem(dataIndex: number): OptionDataItem { getRawDataItem(dataIndex: number): DataTransformDataItem {
// Only built-in transform available. // Only built-in transform available.
throw new Error('not supported'); throw new Error('not supported');
} }
...@@ -152,7 +149,7 @@ export class ExternalSource { ...@@ -152,7 +149,7 @@ export class ExternalSource {
return; return;
} }
retrieveValueFromItem(dataItem: OptionDataItem, dimIndex: DimensionIndex): OptionDataValue { retrieveValueFromItem(dataItem: DataTransformDataItem, dimIndex: DimensionIndex): OptionDataValue {
return; return;
} }
...@@ -169,6 +166,17 @@ function createExternalSource(internalSource: Source, externalTransform: Externa ...@@ -169,6 +166,17 @@ function createExternalSource(internalSource: Source, externalTransform: Externa
const sourceFormat = extSource.sourceFormat = internalSource.sourceFormat; const sourceFormat = extSource.sourceFormat = internalSource.sourceFormat;
const sourceHeaderCount = internalSource.startIndex; const sourceHeaderCount = internalSource.startIndex;
let errMsg = '';
if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) {
// For the logic simplicity in transformer, only 'culumn' is
// supported in data transform. Otherwise, the `dimensionsDefine`
// might be detected by 'row', which probably confuses users.
if (__DEV__) {
errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.';
}
throwError(errMsg);
}
// [MEMO] // [MEMO]
// Create a new dimensions structure for exposing. // Create a new dimensions structure for exposing.
// Do not expose all dimension info to users directly. // Do not expose all dimension info to users directly.
...@@ -219,7 +227,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa ...@@ -219,7 +227,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa
const rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); const rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN);
if (externalTransform.__isBuiltIn) { if (externalTransform.__isBuiltIn) {
extSource.getRawDataItem = function (dataIndex) { extSource.getRawDataItem = function (dataIndex) {
return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex) as DataTransformDataItem;
}; };
extSource.getRawData = bind(getRawData, null, internalSource); extSource.getRawData = bind(getRawData, null, internalSource);
} }
...@@ -231,7 +239,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa ...@@ -231,7 +239,7 @@ function createExternalSource(internalSource: Source, externalTransform: Externa
const rawValueGetter = getRawSourceValueGetter(sourceFormat); const rawValueGetter = getRawSourceValueGetter(sourceFormat);
extSource.retrieveValue = function (dataIndex, dimIndex) { extSource.retrieveValue = function (dataIndex, dimIndex) {
const rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); const rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex) as DataTransformDataItem;
return retrieveValueFromItem(rawItem, dimIndex); return retrieveValueFromItem(rawItem, dimIndex);
}; };
const retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) { const retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) {
...@@ -253,34 +261,31 @@ function createExternalSource(internalSource: Source, externalTransform: Externa ...@@ -253,34 +261,31 @@ function createExternalSource(internalSource: Source, externalTransform: Externa
function getRawData(upstream: Source): Source['data'] { function getRawData(upstream: Source): Source['data'] {
const sourceFormat = upstream.sourceFormat; const sourceFormat = upstream.sourceFormat;
const data = upstream.data;
if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS if (!isSupportedSourceFormat(sourceFormat)) {
|| sourceFormat === SOURCE_FORMAT_OBJECT_ROWS let errMsg = '';
|| !data if (__DEV__) {
|| (isArray(data) && !data.length) errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
) { }
return upstream.data; throwError(errMsg);
} }
let errMsg = ''; return upstream.data;
if (__DEV__) {
errMsg = '`getRawData` is not supported in source format ' + sourceFormat;
}
throwError(errMsg);
} }
function cloneRawData(upstream: Source): Source['data'] { function cloneRawData(upstream: Source): Source['data'] {
const sourceFormat = upstream.sourceFormat; const sourceFormat = upstream.sourceFormat;
const data = upstream.data; const data = upstream.data;
if (!data) { if (!isSupportedSourceFormat(sourceFormat)) {
return data; let errMsg = '';
} if (__DEV__) {
else if (isArray(data) && !data.length) { errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat;
return []; }
throwError(errMsg);
} }
else if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) {
const result = []; const result = [];
for (let i = 0, len = data.length; i < len; i++) { for (let i = 0, len = data.length; i < len; i++) {
// Not strictly clone for performance // Not strictly clone for performance
...@@ -442,39 +447,91 @@ function applySingleDataTransform( ...@@ -442,39 +447,91 @@ function applySingleDataTransform(
} }
} }
return map(resultList, function (result) { return map(resultList, function (result, resultIndex) {
let errMsg = ''; let errMsg = '';
if (!isObject(result)) { if (!isObject(result)) {
if (__DEV__) { if (__DEV__) {
errMsg = 'A transform should not return some empty results.'; errMsg = 'A transform should not return some empty results.';
} }
throwError(errMsg); throwError(errMsg);
} }
let resultData = result.data;
if (resultData != null) { if (!result.data) {
if (!isObject(resultData) && !isArrayLike(resultData)) { if (__DEV__) {
if (__DEV__) { errMsg = 'Transform result data should be not be null or undefined';
errMsg = 'Result data should be object or array in data transform.';
}
throwError(errMsg);
} }
throwError(errMsg);
} }
else {
// Inherit from upstream[0] const sourceFormat = detectSourceFormat(result.data);
resultData = upSourceList[0].data; if (!isSupportedSourceFormat(sourceFormat)) {
if (__DEV__) {
errMsg = 'Transform result data should be array rows or object rows.';
}
throwError(errMsg);
} }
const resultMetaRawOption = inheritSourceMetaRawOption( let resultMetaRawOption: SourceMetaRawOption;
upSourceList[0], const firstUpSource = upSourceList[0];
{
/**
* Intuitively, the end users known the content of the original `dataset.source`,
* calucating the transform result in mind.
* Suppose the original `dataset.source` is:
* ```js
* [
* ['product', '2012', '2013', '2014', '2015'],
* ['AAA', 41.1, 30.4, 65.1, 53.3],
* ['BBB', 86.5, 92.1, 85.7, 83.1],
* ['CCC', 24.1, 67.2, 79.5, 86.4]
* ]
* ```
* The dimension info have to be detected from the source data.
* Some of the transformers (like filter, sort) will follow the dimension info
* of upstream, while others use new dimensions (like aggregate).
* Transformer can output a field `dimensions` to define the its own output dimensions.
* We also allow transformers to ignore the output `dimensions` field, and
* inherit the upstream dimensions definition. It can reduce the burden of handling
* dimensions in transformers.
*
* See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`.
*/
if (
firstUpSource
&& resultIndex === 0
// If transformer returns `dimensions`, it means that the transformer has different
// dimensions definitions. We do not inherit anything from upstream.
&& !result.dimensions
) {
const startIndex = firstUpSource.startIndex;
// We copy the header of upstream to the result becuase:
// (1) The returned data always does not contain header line and can not be used
// as dimension-detection. In this case we can not use "detected dimensions" of
// upstream directly, because it might be detected based on different `seriesLayoutBy`.
// (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`.
// So the original detected header should be add to the result, otherwise they can not be read.
if (startIndex) {
result.data = (firstUpSource.data as []).slice(0, startIndex)
.concat(result.data as []);
}
resultMetaRawOption = {
seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
sourceHeader: startIndex,
dimensions: firstUpSource.metaRawOption.dimensions
};
}
else {
resultMetaRawOption = {
seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN,
sourceHeader: 0, sourceHeader: 0,
dimensions: result.dimensions dimensions: result.dimensions
} };
); }
return createSource( return createSource(
resultData, result.data,
resultMetaRawOption, resultMetaRawOption,
null, null,
null null
...@@ -482,3 +539,6 @@ function applySingleDataTransform( ...@@ -482,3 +539,6 @@ function applySingleDataTransform(
}); });
} }
function isSupportedSourceFormat(sourceFormat: SourceFormat): boolean {
return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS;
}
...@@ -198,10 +198,12 @@ const theme = { ...@@ -198,10 +198,12 @@ const theme = {
}, },
candlestick: { candlestick: {
itemStyle: { itemStyle: {
color: '#FD1050', color: '#f64e56',
color0: '#0CF49B', color0: '#54ea92',
borderColor: '#FD1050', borderColor: '#f64e56',
borderColor0: '#0CF49B' borderColor0: '#54ea92'
// borderColor: '#ca2824',
// borderColor0: '#09a443'
} }
} }
}; };
......
<!DOCTYPE html>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="lib/esl.js"></script>
<script src="lib/config.js"></script>
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
<!-- <script src="ut/lib/canteen.js"></script> -->
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
<style>
</style>
<div id="main0"></div>
<script>
require(['echarts'/*, 'map/js/china' */], function (echarts) {
option = {
legend: {},
tooltip: {},
dataset: {
source: [
['product', '2012', '2013', '2014', '2015'],
['AAA', 41.1, 30.4, 65.1, 53.3],
['BBB', 86.5, 92.1, 85.7, 83.1],
['CCC', 24.1, 67.2, 79.5, 86.4]
]
},
xAxis: [
{type: 'category', gridIndex: 0},
{type: 'category', gridIndex: 1}
],
yAxis: [
{gridIndex: 0},
{gridIndex: 1}
],
grid: [
{bottom: '55%'},
{top: '55%'}
],
series: [
// These series are in the first grid.
{type: 'bar', seriesLayoutBy: 'row'},
{type: 'bar', seriesLayoutBy: 'row'},
// These series are in the second grid.
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1},
{type: 'bar', xAxisIndex: 1, yAxisIndex: 1}
]
};
var chart = testHelper.create(echarts, 'main0', {
title: [
'The legend should be: ',
'"AAA", "BBB", "2012", "2013", "2014"'
],
option: option
// height: 300,
// buttons: [{text: 'btn-txt', onclick: function () {}}],
// recordCanvas: true,
});
});
</script>
</body>
</html>
...@@ -27,6 +27,7 @@ import { ...@@ -27,6 +27,7 @@ import {
import { ComponentMainType } from '../../../src/util/types'; import { ComponentMainType } from '../../../src/util/types';
import Group from 'zrender/src/graphic/Group'; import Group from 'zrender/src/graphic/Group';
import Element from 'zrender/src/Element'; import Element from 'zrender/src/Element';
import GlobalModel from '../../../src/model/Global';
export function createChart(params?: { export function createChart(params?: {
...@@ -137,9 +138,13 @@ export function getViewGroup( ...@@ -137,9 +138,13 @@ export function getViewGroup(
mainType: ComponentMainType, mainType: ComponentMainType,
index?: number index?: number
): Group { ): Group {
const component = chart.getModel().getComponent(mainType, index); const component = getECModel(chart).getComponent(mainType, index);
return component ? chart[ return component ? chart[
mainType === 'series' ? '_chartsMap' : '_componentsMap' mainType === 'series' ? '_chartsMap' : '_componentsMap'
][component.__viewId].group : null; ][component.__viewId].group : null;
} }
export function getECModel(chart: EChartsType): GlobalModel {
// @ts-ignore
return chart.getModel();
}
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
*/ */
import { findEffectedDataZooms } from '../../../../../src/component/dataZoom/helper'; import { findEffectedDataZooms } from '../../../../../src/component/dataZoom/helper';
import { createChart } from '../../../core/utHelper'; import { createChart, getECModel } from '../../../core/utHelper';
import { EChartsType } from '../../../../../src/echarts'; import { EChartsType } from '../../../../../src/echarts';
...@@ -50,12 +50,12 @@ describe('dataZoom/helper', function () { ...@@ -50,12 +50,12 @@ describe('dataZoom/helper', function () {
}); });
const payload = { type: 'dataZoom', dataZoomIndex: 0 }; const payload = { type: 'dataZoom', dataZoomIndex: 0 };
const dzModels = findEffectedDataZooms(chart.getModel(), payload); const dzModels = findEffectedDataZooms(getECModel(chart), payload);
expect(dzModels.length === 3); expect(dzModels.length === 3);
expect(dzModels[0] === chart.getModel().getComponent('dataZoom', 0)).toEqual(true); expect(dzModels[0] === getECModel(chart).getComponent('dataZoom', 0)).toEqual(true);
expect(dzModels[1] === chart.getModel().getComponent('dataZoom', 3)).toEqual(true); expect(dzModels[1] === getECModel(chart).getComponent('dataZoom', 3)).toEqual(true);
expect(dzModels[2] === chart.getModel().getComponent('dataZoom', 2)).toEqual(true); expect(dzModels[2] === getECModel(chart).getComponent('dataZoom', 2)).toEqual(true);
}); });
it('findLinkedNodes_crossXY', function () { it('findLinkedNodes_crossXY', function () {
...@@ -72,13 +72,13 @@ describe('dataZoom/helper', function () { ...@@ -72,13 +72,13 @@ describe('dataZoom/helper', function () {
}); });
const payload = { type: 'dataZoom', dataZoomIndex: 0 }; const payload = { type: 'dataZoom', dataZoomIndex: 0 };
const dzModels = findEffectedDataZooms(chart.getModel(), payload); const dzModels = findEffectedDataZooms(getECModel(chart), payload);
expect(dzModels.length === 4); expect(dzModels.length === 4);
expect(dzModels[0] === chart.getModel().getComponent('dataZoom', 0)).toEqual(true); expect(dzModels[0] === getECModel(chart).getComponent('dataZoom', 0)).toEqual(true);
expect(dzModels[1] === chart.getModel().getComponent('dataZoom', 1)).toEqual(true); expect(dzModels[1] === getECModel(chart).getComponent('dataZoom', 1)).toEqual(true);
expect(dzModels[2] === chart.getModel().getComponent('dataZoom', 2)).toEqual(true); expect(dzModels[2] === getECModel(chart).getComponent('dataZoom', 2)).toEqual(true);
expect(dzModels[3] === chart.getModel().getComponent('dataZoom', 3)).toEqual(true); expect(dzModels[3] === getECModel(chart).getComponent('dataZoom', 3)).toEqual(true);
}); });
it('findLinkedNodes_emptySourceModel', function () { it('findLinkedNodes_emptySourceModel', function () {
...@@ -95,7 +95,7 @@ describe('dataZoom/helper', function () { ...@@ -95,7 +95,7 @@ describe('dataZoom/helper', function () {
}); });
const payload = { type: 'other' }; const payload = { type: 'other' };
const dzModels = findEffectedDataZooms(chart.getModel(), payload); const dzModels = findEffectedDataZooms(getECModel(chart), payload);
expect(dzModels.length === 0); expect(dzModels.length === 0);
}); });
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import { createChart, getGraphicElements } from '../../../core/utHelper'; import { createChart, getGraphicElements, getECModel } from '../../../core/utHelper';
// import { imageURI } from './setOptionImageURI'; // import { imageURI } from './setOptionImageURI';
import { EChartsType } from '../../../../../src/echarts'; import { EChartsType } from '../../../../../src/echarts';
import Element from 'zrender/src/Element'; import Element from 'zrender/src/Element';
...@@ -338,8 +338,8 @@ describe('graphic_setOption', function () { ...@@ -338,8 +338,8 @@ describe('graphic_setOption', function () {
] ]
}); });
expect(!!chart.getModel().getComponent('graphic')).toEqual(true); expect(!!getECModel(chart).getComponent('graphic')).toEqual(true);
expect(chart.getModel().getComponent('graphic', 1) == null).toEqual(true); expect(getECModel(chart).getComponent('graphic', 1) == null).toEqual(true);
}); });
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* under the License. * under the License.
*/ */
import { createChart } from '../../../core/utHelper'; import { createChart, getECModel } from '../../../core/utHelper';
import { EChartsType } from '../../../../../src/echarts'; import { EChartsType } from '../../../../../src/echarts';
import { EChartsFullOption } from '../../../../../src/option'; import { EChartsFullOption } from '../../../../../src/option';
import { ContinousVisualMapOption } from '../../../../../src/component/visualMap/ContinuousModel'; import { ContinousVisualMapOption } from '../../../../../src/component/visualMap/ContinuousModel';
...@@ -256,7 +256,7 @@ describe('vsiaulMap_setOption', function () { ...@@ -256,7 +256,7 @@ describe('vsiaulMap_setOption', function () {
] ]
}); });
const ecModel = chart.getModel(); const ecModel = getECModel(chart);
function getVisual(idx: number, visualType: 'color' | 'opacity' | 'symbol') { function getVisual(idx: number, visualType: 'color' | 'opacity' | 'symbol') {
return (ecModel.getComponent('visualMap', idx) as VisualMapModel) return (ecModel.getComponent('visualMap', idx) as VisualMapModel)
......
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { EChartsType } from '../../../../src/echarts';
import { createChart, removeChart, getECModel } from '../../core/utHelper';
import { EChartsFullOption } from '../../../../src/option';
import { retrieveRawValue } from '../../../../src/data/helper/dataProvider';
describe('dataTransform', function () {
let chart: EChartsType;
beforeEach(function () {
chart = createChart({
width: 200,
height: 150
});
});
afterEach(function () {
removeChart(chart);
});
function makeDatasetSourceDetection() {
return [
['product', '2012', '2013', '2014', '2015'],
['AAA', 41.1, 30.4, 65.1, 53.3],
['BBB', 86.5, 92.1, 85.7, 83.1],
['CCC', 24.1, 67.2, 79.5, 86.4]
];
}
function makeDatasetSourceNonDetectionByRow() {
return {
dimensions: ['2012', '2013', '2014', '2015'],
source: [
[41.1, 30.4, 65.1, 53.3],
[86.5, 92.1, 85.7, 83.1],
[24.1, 67.2, 79.5, 86.4]
]
};
}
it('forbid_seriesLayoutBy_row', function () {
const option: EChartsFullOption = {
dataset: [{
source: makeDatasetSourceDetection(),
// This config should cause error thrown.
seriesLayoutBy: 'row'
}, {
transform: { type: 'filter', config: { dimension: 0, ne: '' } }
}],
xAxis: { type: 'category' },
yAxis: {},
series: { type: 'bar', datasetIndex: 1 }
};
expect(() => {
chart.setOption(option);
}).toThrowError(/column/);
});
it('seriesLayoutBy_changed_no_transform', function () {
const option: EChartsFullOption = {
dataset: {
source: makeDatasetSourceDetection()
},
xAxis: { type: 'category' },
yAxis: {},
series: { type: 'bar', seriesLayoutBy: 'row' }
};
chart.setOption(option);
const listData = getECModel(chart).getSeries()[0].getData();
expect(listData.getDimension(1)).toEqual('AAA');
expect(listData.getDimension(2)).toEqual('BBB');
expect(listData.getDimension(3)).toEqual('CCC');
});
[{
transform:
{ type: 'filter', config: { dimension: 'product', '!=': 'XXX' } }
}, {
transform: [
{ type: 'filter', config: { dimension: 'product', '!=': 'XXX' } },
{ type: 'filter', config: { dimension: 'product', '!=': 'XXX' } }
]
}].forEach((dataset1, itIdx) => {
it(`seriesLayoutBy_changed_transform_detection_${itIdx}`, function () {
const option: EChartsFullOption = {
dataset: [{
source: makeDatasetSourceDetection()
}, dataset1],
xAxis: { type: 'category' },
yAxis: {},
series: { type: 'bar', datasetIndex: 1, seriesLayoutBy: 'row' }
};
chart.setOption(option);
const listData = getECModel(chart).getSeries()[0].getData();
expect(listData.getDimension(1)).toEqual('AAA');
expect(listData.getDimension(2)).toEqual('BBB');
expect(listData.getDimension(3)).toEqual('CCC');
expect(listData.get('product', 0)).toEqual(0);
expect(retrieveRawValue(listData, 0, 'product')).toEqual('2012');
expect(listData.get('product', 1)).toEqual(1);
expect(retrieveRawValue(listData, 1, 'product')).toEqual('2013');
});
});
[{
transform:
{ type: 'filter', config: { dimension: 0, '!=': 'XXX' } }
}, {
transform: [
{ type: 'filter', config: { dimension: 0, '!=': 'XXX' } },
{ type: 'filter', config: { dimension: 0, '!=': 'XXX' } }
]
}].forEach((dataset1, itIdx) => {
it(`seriesLayoutBy_changed_transform_non_detection_${itIdx}`, function () {
const sourceWrap = makeDatasetSourceNonDetectionByRow();
const option: EChartsFullOption = {
dataset: [{
dimensions: sourceWrap.dimensions,
source: sourceWrap.source
}, dataset1],
xAxis: {},
yAxis: {},
series: { type: 'bar', datasetIndex: 1, seriesLayoutBy: 'row' }
};
chart.setOption(option);
const listData = getECModel(chart).getSeries()[0].getData();
expect(listData.get(listData.getDimension(0), 0)).toEqual(41.1);
expect(listData.get(listData.getDimension(0), 1)).toEqual(30.4);
});
});
[{
transform:
{ type: 'filter', config: { dimension: '2012', '!=': 'XXX' } }
}, {
transform: [
{ type: 'filter', config: { dimension: '2012', '!=': 'XXX' } },
{ type: 'filter', config: { dimension: '2012', '!=': 'XXX' } }
]
}].forEach((dataset1, itIdx) => {
it(`inherit_detected_dimensions_${itIdx}`, function () {
const option: EChartsFullOption = {
dataset: [{
source: makeDatasetSourceDetection()
}, dataset1],
xAxis: { type: 'category' },
yAxis: {},
series: { type: 'bar', datasetIndex: 1 }
};
chart.setOption(option);
const listData = getECModel(chart).getSeries()[0].getData();
expect(listData.getDimension(0)).toEqual('product');
expect(listData.getDimension(1)).toEqual('2012');
expect(listData.getDimension(2)).toEqual('2013');
});
});
});
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
*/ */
import { EChartsType } from '../../../../src/echarts'; import { EChartsType } from '../../../../src/echarts';
import { createChart } from '../../core/utHelper'; import { createChart, getECModel } from '../../core/utHelper';
import { ComponentMainType, ParsedValue } from '../../../../src/util/types'; import { ComponentMainType, ParsedValue } from '../../../../src/util/types';
import SeriesModel from '../../../../src/model/Series'; import SeriesModel from '../../../../src/model/Series';
import ComponentModel from '../../../../src/model/Component'; import ComponentModel from '../../../../src/model/Component';
...@@ -39,11 +39,11 @@ describe('modelAndOptionMapping', function () { ...@@ -39,11 +39,11 @@ describe('modelAndOptionMapping', function () {
} }
function getSeries(chart: EChartsType, seriesIndex: number): SeriesModel { function getSeries(chart: EChartsType, seriesIndex: number): SeriesModel {
return chart.getModel().getComponent('series', seriesIndex) as SeriesModel; return getECModel(chart).getComponent('series', seriesIndex) as SeriesModel;
} }
function getModel(chart: EChartsType, type: ComponentMainType, index: number): ComponentModel { function getModel(chart: EChartsType, type: ComponentMainType, index: number): ComponentModel {
return chart.getModel().getComponent(type, index); return getECModel(chart).getComponent(type, index);
} }
function countSeries(chart: EChartsType): number { function countSeries(chart: EChartsType): number {
...@@ -54,7 +54,7 @@ describe('modelAndOptionMapping', function () { ...@@ -54,7 +54,7 @@ describe('modelAndOptionMapping', function () {
// FIXME // FIXME
// access private // access private
// @ts-ignore // @ts-ignore
return chart.getModel()._componentsMap.get(type).length; return getECModel(chart)._componentsMap.get(type).length;
} }
function getChartView(chart: EChartsType, series: SeriesModel): ChartView { function getChartView(chart: EChartsType, series: SeriesModel): ChartView {
...@@ -801,14 +801,14 @@ describe('modelAndOptionMapping', function () { ...@@ -801,14 +801,14 @@ describe('modelAndOptionMapping', function () {
] ]
}; };
chart.setOption(option); chart.setOption(option);
expect(chart.getModel().option.backgroundColor).toEqual('rgba(1,1,1,1)'); expect(getECModel(chart).option.backgroundColor).toEqual('rgba(1,1,1,1)');
// Not merge // Not merge
chart.setOption({ chart.setOption({
backgroundColor: '#fff' backgroundColor: '#fff'
}, true); }, true);
expect(chart.getModel().option.backgroundColor).toEqual('#fff'); expect(getECModel(chart).option.backgroundColor).toEqual('#fff');
}); });
it('innerId', function () { it('innerId', function () {
......
...@@ -23,7 +23,7 @@ import SeriesModel from '../../../../src/model/Series'; ...@@ -23,7 +23,7 @@ import SeriesModel from '../../../../src/model/Series';
import { ParsedValue } from '../../../../src/util/types'; import { ParsedValue } from '../../../../src/util/types';
import { LegendOption } from '../../../../src/component/legend/LegendModel'; import { LegendOption } from '../../../../src/component/legend/LegendModel';
import TimelineModel from '../../../../src/component/timeline/TimelineModel'; import TimelineModel from '../../../../src/component/timeline/TimelineModel';
import { createChart } from '../../core/utHelper'; import { createChart, getECModel } from '../../core/utHelper';
import { EChartsFullOption } from '../../../../src/option'; import { EChartsFullOption } from '../../../../src/option';
...@@ -33,13 +33,13 @@ describe('timelineMediaOptions', function () { ...@@ -33,13 +33,13 @@ describe('timelineMediaOptions', function () {
return getSeries(chart, seriesIndex).getData().get('y', 0); return getSeries(chart, seriesIndex).getData().get('y', 0);
} }
function getSeries(chart: EChartsType, seriesIndex: number): SeriesModel { function getSeries(chart: EChartsType, seriesIndex: number): SeriesModel {
return chart.getModel().getComponent('series', seriesIndex) as SeriesModel; return getECModel(chart).getComponent('series', seriesIndex) as SeriesModel;
} }
function getLegendOption(chart: EChartsType): LegendOption { function getLegendOption(chart: EChartsType): LegendOption {
return chart.getModel().getComponent('legend', 0).option; return getECModel(chart).getComponent('legend', 0).option;
} }
function getTimelineComponent(chart: EChartsType): TimelineModel { function getTimelineComponent(chart: EChartsType): TimelineModel {
return chart.getModel().getComponent('timeline', 0) as TimelineModel; return getECModel(chart).getComponent('timeline', 0) as TimelineModel;
} }
let chart: EChartsType; let chart: EChartsType;
...@@ -388,7 +388,7 @@ describe('timelineMediaOptions', function () { ...@@ -388,7 +388,7 @@ describe('timelineMediaOptions', function () {
}; };
chart.setOption(option); chart.setOption(option);
let ecModel = chart.getModel(); let ecModel = getECModel(chart);
expect(getData0(chart, 0)).toEqual(1111); expect(getData0(chart, 0)).toEqual(1111);
expect(getData0(chart, 1)).toEqual(2222); expect(getData0(chart, 1)).toEqual(2222);
...@@ -410,7 +410,7 @@ describe('timelineMediaOptions', function () { ...@@ -410,7 +410,7 @@ describe('timelineMediaOptions', function () {
}] }]
}); });
ecModel = chart.getModel(); ecModel = getECModel(chart);
const optionGotten = ecModel.getOption(); const optionGotten = ecModel.getOption();
expect(optionGotten.backgroundColor).toEqual('#987654'); expect(optionGotten.backgroundColor).toEqual('#987654');
expect(getData0(chart, 0)).toEqual(1111); expect(getData0(chart, 0)).toEqual(1111);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* under the License. * under the License.
*/ */
import { createChart, isValueFinite } from '../../core/utHelper'; import { createChart, isValueFinite, getECModel } from '../../core/utHelper';
import { EChartsType } from '../../../../src/echarts'; import { EChartsType } from '../../../../src/echarts';
import CartesianAxisModel from '../../../../src/coord/cartesian/AxisModel'; import CartesianAxisModel from '../../../../src/coord/cartesian/AxisModel';
import IntervalScale from '../../../../src/scale/Interval'; import IntervalScale from '../../../../src/scale/Interval';
...@@ -57,7 +57,7 @@ describe('scale_interval', function () { ...@@ -57,7 +57,7 @@ describe('scale_interval', function () {
series: [{type: 'line', data: []}] series: [{type: 'line', data: []}]
}); });
const yAxis = chart.getModel().getComponent('yAxis', 0) as CartesianAxisModel; const yAxis = getECModel(chart).getComponent('yAxis', 0) as CartesianAxisModel;
const scale = yAxis.axis.scale; const scale = yAxis.axis.scale;
const ticks = scale.getTicks(); const ticks = scale.getTicks();
...@@ -91,7 +91,7 @@ describe('scale_interval', function () { ...@@ -91,7 +91,7 @@ describe('scale_interval', function () {
] ]
}); });
const yAxis = chart.getModel().getComponent('yAxis', 0) as CartesianAxisModel; const yAxis = getECModel(chart).getComponent('yAxis', 0) as CartesianAxisModel;
const scale = yAxis.axis.scale as IntervalScale; const scale = yAxis.axis.scale as IntervalScale;
const ticks = scale.getTicks(); const ticks = scale.getTicks();
const labels = yAxis.axis.getViewLabels().map(function (item) { const labels = yAxis.axis.getViewLabels().map(function (item) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册