transform.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  2. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  3. return new (P || (P = Promise))(function (resolve, reject) {
  4. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  5. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  6. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  7. step((generator = generator.apply(thisArg, _arguments || [])).next());
  8. });
  9. };
  10. var __rest = (this && this.__rest) || function (s, e) {
  11. var t = {};
  12. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
  13. t[p] = s[p];
  14. if (s != null && typeof Object.getOwnPropertySymbols === "function")
  15. for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  16. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
  17. t[p[i]] = s[p[i]];
  18. }
  19. return t;
  20. };
  21. import { deepMix } from '@antv/util';
  22. import { format } from 'd3-format';
  23. import { indexOf, mapObject } from '../utils/array';
  24. import { composeAsync, defined, isStrictObject, isUnset, } from '../utils/helper';
  25. import { isFullTooltip } from '../utils/mark';
  26. import { useLibrary } from './library';
  27. import { createColumnOf } from './mark';
  28. import { isPosition } from './scale';
  29. // @todo Add more defaults.
  30. export function applyDefaults(I, mark, context) {
  31. const { encode = {}, scale = {}, transform = [] } = mark, rest = __rest(mark, ["encode", "scale", "transform"]);
  32. return [I, Object.assign(Object.assign({}, rest), { encode, scale, transform })];
  33. }
  34. export function applyDataTransform(I, mark, context) {
  35. return __awaiter(this, void 0, void 0, function* () {
  36. const { library } = context;
  37. const { data } = mark;
  38. const [useData] = useLibrary('data', library);
  39. const descriptor = normalizedDataSource(data);
  40. const { transform: T = [] } = descriptor, connector = __rest(descriptor, ["transform"]);
  41. const transform = [connector, ...T];
  42. const transformFunctions = transform.map(useData);
  43. const transformedData = yield composeAsync(transformFunctions)(data);
  44. return [
  45. Array.isArray(transformedData) ? indexOf(transformedData) : [],
  46. Object.assign(Object.assign({}, mark), { data: transformedData }),
  47. ];
  48. });
  49. }
  50. export function flatEncode(I, mark, context) {
  51. const { encode } = mark;
  52. if (!encode)
  53. return [I, mark];
  54. const flattenEncode = {};
  55. for (const [key, value] of Object.entries(encode)) {
  56. if (Array.isArray(value)) {
  57. for (let i = 0; i < value.length; i++) {
  58. const name = `${key}${i === 0 ? '' : i}`;
  59. flattenEncode[name] = value[i];
  60. }
  61. }
  62. else {
  63. flattenEncode[key] = value;
  64. }
  65. }
  66. return [I, Object.assign(Object.assign({}, mark), { encode: flattenEncode })];
  67. }
  68. export function inferChannelsType(I, mark, context) {
  69. const { encode, data } = mark;
  70. if (!encode)
  71. return [I, mark];
  72. const typedEncode = mapObject(encode, (channel) => {
  73. if (isTypedChannel(channel))
  74. return channel;
  75. const type = inferChannelType(data, channel);
  76. return { type, value: channel };
  77. });
  78. return [I, Object.assign(Object.assign({}, mark), { encode: typedEncode })];
  79. }
  80. export function maybeVisualChannel(I, mark, context) {
  81. const { encode } = mark;
  82. if (!encode)
  83. return [I, mark];
  84. const newEncode = mapObject(encode, (channel, name) => {
  85. const { type } = channel;
  86. if (type !== 'constant' || isPosition(name))
  87. return channel;
  88. return Object.assign(Object.assign({}, channel), { constant: true });
  89. });
  90. return [I, Object.assign(Object.assign({}, mark), { encode: newEncode })];
  91. }
  92. export function extractColumns(I, mark, context) {
  93. const { encode, data } = mark;
  94. if (!encode)
  95. return [I, mark];
  96. const { library } = context;
  97. const columnOf = createColumnOf(library);
  98. const valuedEncode = mapObject(encode, (channel) => columnOf(data, channel));
  99. return [I, Object.assign(Object.assign({}, mark), { encode: valuedEncode })];
  100. }
  101. /**
  102. * Normalize mark.tooltip to {title, items}.
  103. */
  104. export function normalizeTooltip(I, mark, context) {
  105. const { tooltip = {} } = mark;
  106. if (isUnset(tooltip))
  107. return [I, mark];
  108. if (Array.isArray(tooltip)) {
  109. return [I, Object.assign(Object.assign({}, mark), { tooltip: { items: tooltip } })];
  110. }
  111. if (isStrictObject(tooltip) && isFullTooltip(tooltip)) {
  112. return [I, Object.assign(Object.assign({}, mark), { tooltip })];
  113. }
  114. return [I, Object.assign(Object.assign({}, mark), { tooltip: { items: [tooltip] } })];
  115. }
  116. export function extractTooltip(I, mark, context) {
  117. const { data, encode, tooltip = {} } = mark;
  118. if (isUnset(tooltip))
  119. return [I, mark];
  120. const valueOf = (item) => {
  121. if (!item)
  122. return item;
  123. if (typeof item === 'string') {
  124. return I.map((i) => ({ name: item, value: data[i][item] }));
  125. }
  126. if (isStrictObject(item)) {
  127. const { field, channel, color, name = field, valueFormatter = (d) => d, } = item;
  128. // Support d3-format.
  129. const normalizedValueFormatter = typeof valueFormatter === 'string'
  130. ? format(valueFormatter)
  131. : valueFormatter;
  132. // Field name.
  133. const definedChannel = channel && encode[channel];
  134. const channelField = definedChannel && encode[channel].field;
  135. const name1 = name || channelField || channel;
  136. const values = [];
  137. for (const i of I) {
  138. const value1 = field
  139. ? data[i][field]
  140. : definedChannel
  141. ? encode[channel].value[i]
  142. : null;
  143. values[i] = {
  144. name: name1,
  145. color,
  146. value: normalizedValueFormatter(value1),
  147. };
  148. }
  149. return values;
  150. }
  151. if (typeof item === 'function') {
  152. const values = [];
  153. for (const i of I) {
  154. const v = item(data[i], i, data, encode);
  155. if (isStrictObject(v))
  156. values[i] = v;
  157. else
  158. values[i] = { value: v };
  159. }
  160. return values;
  161. }
  162. return item;
  163. };
  164. const { title, items = [] } = tooltip, rest = __rest(tooltip, ["title", "items"]);
  165. const newTooltip = Object.assign({ title: valueOf(title), items: Array.isArray(items) ? items.map(valueOf) : [] }, rest);
  166. return [I, Object.assign(Object.assign({}, mark), { tooltip: newTooltip })];
  167. }
  168. export function maybeArrayField(I, mark, context) {
  169. const { encode } = mark, rest = __rest(mark, ["encode"]);
  170. if (!encode)
  171. return [I, mark];
  172. const columns = Object.entries(encode);
  173. const arrayColumns = columns
  174. .filter(([, channel]) => {
  175. const { value: V } = channel;
  176. return Array.isArray(V[0]);
  177. })
  178. .flatMap(([key, V]) => {
  179. const columns = [[key, new Array(I.length).fill(undefined)]];
  180. const { value: rows } = V, rest = __rest(V, ["value"]);
  181. for (let i = 0; i < rows.length; i++) {
  182. const row = rows[i];
  183. if (Array.isArray(row)) {
  184. for (let j = 0; j < row.length; j++) {
  185. const column = columns[j] || [
  186. `${key}${j}`,
  187. new Array(I).fill(undefined),
  188. ];
  189. column[1][i] = row[j];
  190. columns[j] = column;
  191. }
  192. }
  193. }
  194. return columns.map(([key, value]) => [
  195. key,
  196. Object.assign({ type: 'column', value }, rest),
  197. ]);
  198. });
  199. const newEncode = Object.fromEntries([...columns, ...arrayColumns]);
  200. return [I, Object.assign(Object.assign({}, rest), { encode: newEncode })];
  201. }
  202. export function addGuideToScale(I, mark, context) {
  203. const { axis = {}, legend = {}, slider = {}, scrollbar = {} } = mark;
  204. const normalize = (guide, channel) => {
  205. if (typeof guide === 'boolean')
  206. return guide ? {} : null;
  207. const eachGuide = guide[channel];
  208. return eachGuide === undefined || eachGuide ? eachGuide : null;
  209. };
  210. const axisChannels = typeof axis === 'object'
  211. ? Array.from(new Set(['x', 'y', ...Object.keys(axis)]))
  212. : ['x', 'y'];
  213. deepMix(mark, {
  214. scale: Object.assign(Object.assign({}, Object.fromEntries(axisChannels.map((channel) => {
  215. const scrollbarOptions = normalize(scrollbar, channel);
  216. return [
  217. channel,
  218. Object.assign({ guide: normalize(axis, channel), slider: normalize(slider, channel), scrollbar: scrollbarOptions }, (scrollbarOptions && {
  219. ratio: scrollbarOptions.ratio === undefined
  220. ? 0.5
  221. : scrollbarOptions.ratio,
  222. })),
  223. ];
  224. }))), { color: { guide: normalize(legend, 'color') }, size: { guide: normalize(legend, 'size') }, shape: { guide: normalize(legend, 'shape') },
  225. // fixme: opacity is conflict with DisplayObject.opacity
  226. // to be confirm.
  227. opacity: { guide: normalize(legend, 'opacity') } }),
  228. });
  229. return [I, mark];
  230. }
  231. export function maybeNonAnimate(I, mark, context) {
  232. const { animate } = mark;
  233. if (animate || animate === undefined)
  234. return [I, mark];
  235. deepMix(mark, {
  236. animate: {
  237. enter: { type: null },
  238. exit: { type: null },
  239. update: { type: null },
  240. },
  241. });
  242. return [I, mark];
  243. }
  244. function isTypedChannel(channel) {
  245. if (typeof channel !== 'object' ||
  246. channel instanceof Date ||
  247. channel === null) {
  248. return false;
  249. }
  250. const { type } = channel;
  251. return defined(type);
  252. }
  253. function inferChannelType(data, channel) {
  254. if (typeof channel === 'function')
  255. return 'transform';
  256. if (typeof channel === 'string' && isField(data, channel))
  257. return 'field';
  258. return 'constant';
  259. }
  260. function isField(data, value) {
  261. if (!Array.isArray(data))
  262. return false;
  263. return data.some((d) => d[value] !== undefined);
  264. }
  265. function normalizedDataSource(data) {
  266. // Return null as a placeholder.
  267. if (!data)
  268. return { type: 'inline', value: null };
  269. if (Array.isArray(data))
  270. return { type: 'inline', value: data };
  271. const { type = 'inline' } = data, rest = __rest(data, ["type"]);
  272. return Object.assign(Object.assign({}, rest), { type });
  273. }
  274. //# sourceMappingURL=transform.js.map