1 | import { each, mix, isNil, isFunction, isNumber, valuesOfKey, getRange } from '@antv/util';
|
2 | import { registerTickMethod, Scale, getScale, ScaleConfig } from '@antv/scale';
|
3 | import CatTick from './scale/cat-tick';
|
4 | import LinearTick from './scale/linear-tick';
|
5 |
|
6 |
|
7 | registerTickMethod('cat', CatTick);
|
8 | registerTickMethod('time-cat', CatTick);
|
9 |
|
10 | registerTickMethod('wilkinson-extended', LinearTick);
|
11 |
|
12 | export type ScaleOption = { type?: string; justifyContent?: boolean } & ScaleConfig;
|
13 |
|
14 | class ScaleController {
|
15 |
|
16 | private data: any;
|
17 |
|
18 | private options: { [field: string]: ScaleOption };
|
19 |
|
20 | private scales: { [field: string]: Scale };
|
21 |
|
22 | constructor(data) {
|
23 | this.data = data;
|
24 | this.options = {};
|
25 | this.scales = {};
|
26 | }
|
27 |
|
28 | private _getType(option: ScaleOption) {
|
29 | const { type, values, field } = option;
|
30 | if (type) {
|
31 | return type;
|
32 | }
|
33 | if (isNumber(field) || (isNil(values[0]) && field)) {
|
34 | return 'identity';
|
35 | }
|
36 | if (typeof values[0] === 'number') {
|
37 | return 'linear';
|
38 | }
|
39 | return 'cat';
|
40 | }
|
41 |
|
42 | private _getOption(option: ScaleOption) {
|
43 | const { values, field, justifyContent } = option;
|
44 | const type = this._getType(option);
|
45 |
|
46 | option.type = type;
|
47 |
|
48 |
|
49 | if (type === 'identity') {
|
50 | option.field = field.toString();
|
51 | option.values = [field];
|
52 | return option;
|
53 | }
|
54 |
|
55 | if (type === 'linear') {
|
56 |
|
57 | if (typeof option.nice !== 'boolean') {
|
58 | option.nice = true;
|
59 | }
|
60 |
|
61 | const { min, max } = getRange(values);
|
62 | if (isNil(option.min)) {
|
63 | option.min = min;
|
64 | }
|
65 | if (isNil(option.max)) {
|
66 | option.max = max;
|
67 | }
|
68 | return option;
|
69 | }
|
70 |
|
71 | if (type === 'cat' || type === 'timeCat') {
|
72 | if (option.range) {
|
73 | return option;
|
74 | }
|
75 | const count = values.length;
|
76 | let range = [0, 1];
|
77 |
|
78 | if (count === 1) {
|
79 | range = [0.5, 1];
|
80 | } else if (justifyContent) {
|
81 |
|
82 | const offset = (1 / count) * 0.5;
|
83 | range = [offset, 1 - offset];
|
84 | } else {
|
85 |
|
86 | const offset = 1 / count;
|
87 | range = [0, 1 - offset];
|
88 | }
|
89 | option.range = range;
|
90 | }
|
91 |
|
92 | return option;
|
93 | }
|
94 |
|
95 | private createScale(option) {
|
96 | const { type } = option;
|
97 | if (isFunction(type)) {
|
98 | return new type(option);
|
99 | }
|
100 | const ScaleClass = getScale(type);
|
101 | return new ScaleClass(option);
|
102 | }
|
103 |
|
104 |
|
105 | setScale(field: string, option?: ScaleOption) {
|
106 | const { options, scales } = this;
|
107 | options[field] = mix({}, options[field], option);
|
108 |
|
109 | if (scales[field]) {
|
110 | delete scales[field];
|
111 | }
|
112 | }
|
113 |
|
114 | create(options: { [k: string]: ScaleOption }) {
|
115 | this.update(options);
|
116 | }
|
117 |
|
118 | update(options: { [k: string]: ScaleOption }) {
|
119 | if (!options) return;
|
120 | each(options, (option: ScaleOption, field: string) => {
|
121 | this.setScale(field, option);
|
122 | });
|
123 |
|
124 | this.scales = {
|
125 | ...this.scales,
|
126 | };
|
127 | }
|
128 |
|
129 | changeData(data) {
|
130 | this.data = data;
|
131 | this.scales = {};
|
132 | }
|
133 |
|
134 | getData() {
|
135 | return this.data;
|
136 | }
|
137 |
|
138 | getScale(field: string): Scale {
|
139 | const { scales, options, data } = this;
|
140 |
|
141 | const scale = scales[field];
|
142 | if (scale) {
|
143 | return scale;
|
144 | }
|
145 | const option = options[field];
|
146 | if (!option) {
|
147 | return null;
|
148 | }
|
149 | const values = option.values ? option.values : data ? valuesOfKey(data, field) : [];
|
150 | const scaleOption = this._getOption({
|
151 | ...option,
|
152 | field,
|
153 | values,
|
154 | });
|
155 | const newScale = this.createScale(scaleOption);
|
156 | scales[field] = newScale;
|
157 | return newScale;
|
158 | }
|
159 |
|
160 | getScales() {
|
161 | const { options, scales } = this;
|
162 | each(options, (option, field: string) => {
|
163 | this.getScale(field);
|
164 | });
|
165 | return scales;
|
166 | }
|
167 |
|
168 | adjustStartZero(scale: Scale) {
|
169 | const { options } = this;
|
170 | const { field, min, max } = scale;
|
171 | const option = options[field];
|
172 |
|
173 | if (option && option.min) {
|
174 | return;
|
175 | }
|
176 | if (min > 0) {
|
177 | scale.change({
|
178 | min: 0,
|
179 | });
|
180 | } else if (max < 0) {
|
181 | scale.change({
|
182 | max: 0,
|
183 | });
|
184 | }
|
185 | }
|
186 |
|
187 |
|
188 | adjustPieScale(scale: Scale) {
|
189 | const { options } = this;
|
190 | const { field } = scale;
|
191 | const option = options[field];
|
192 |
|
193 | if (option && !isNil(option.nice)) {
|
194 | return null;
|
195 | }
|
196 | scale.change({
|
197 | nice: false,
|
198 | });
|
199 | }
|
200 |
|
201 |
|
202 | getZeroValue(scale) {
|
203 | const { min, max } = scale;
|
204 | let value;
|
205 |
|
206 | if (min >= 0) {
|
207 | value = min;
|
208 | } else if (max <= 0) {
|
209 | value = max;
|
210 | } else {
|
211 | value = 0;
|
212 | }
|
213 | return scale.scale(value);
|
214 | }
|
215 | }
|
216 |
|
217 | export default ScaleController;
|