legendCategory.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. "use strict";
  2. var __rest = (this && this.__rest) || function (s, e) {
  3. var t = {};
  4. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
  5. t[p] = s[p];
  6. if (s != null && typeof Object.getOwnPropertySymbols === "function")
  7. for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
  8. if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
  9. t[p[i]] = s[p[i]];
  10. }
  11. return t;
  12. };
  13. Object.defineProperty(exports, "__esModule", { value: true });
  14. exports.LegendCategory = void 0;
  15. const gui_1 = require("@antv/gui");
  16. const util_1 = require("@antv/util");
  17. const d3_format_1 = require("d3-format");
  18. const marker_1 = require("../utils/marker");
  19. const utils_1 = require("./utils");
  20. function inferLayout(position, gridRow, gridCol) {
  21. const [gridRowLimit, gridColLimit] = [gridRow || 100, gridCol || 100];
  22. switch (position) {
  23. case 'top':
  24. case 'bottom':
  25. case 'top-left':
  26. case 'top-right':
  27. case 'bottom-left':
  28. case 'bottom-right':
  29. return [1, gridColLimit];
  30. case 'left':
  31. case 'right':
  32. return [gridRowLimit, 1];
  33. default:
  34. return [gridRow, gridCol];
  35. }
  36. }
  37. function createShape(shape, library, coordinate, theme, style = {}) {
  38. var _a, _b;
  39. const marker = ((_b = (_a = library[`shape.${shape}`]) === null || _a === void 0 ? void 0 : _a.props) === null || _b === void 0 ? void 0 : _b.defaultMarker) ||
  40. (0, util_1.last)(shape.split('.'));
  41. return () => (0, marker_1.useMarker)(marker, style)(0, 0, 6);
  42. }
  43. function inferShape(scales, markState) {
  44. const shapeScale = (0, utils_1.scaleOf)(scales, 'shape');
  45. const colorScale = (0, utils_1.scaleOf)(scales, 'color');
  46. // infer the main shape if multiple marks are used
  47. const shapes = [];
  48. for (const [mark, state] of markState) {
  49. const namespace = mark.type;
  50. const domain = (colorScale === null || colorScale === void 0 ? void 0 : colorScale.getOptions().domain.length) > 0
  51. ? colorScale === null || colorScale === void 0 ? void 0 : colorScale.getOptions().domain
  52. : state.data;
  53. const shape = domain.map((d, i) => {
  54. var _a;
  55. if (shapeScale)
  56. return shapeScale.map(d || 'point');
  57. return ((_a = mark === null || mark === void 0 ? void 0 : mark.style) === null || _a === void 0 ? void 0 : _a.shape) || state.defaultShape || 'point';
  58. });
  59. if (typeof namespace === 'string')
  60. shapes.push([namespace, shape]);
  61. }
  62. if (shapes.length === 0)
  63. return ['point', ['point']];
  64. if (shapes.length === 1)
  65. return shapes[0];
  66. if (!shapeScale)
  67. return shapes[0];
  68. // Evaluate the maximum likelihood of shape
  69. const { range } = shapeScale.getOptions();
  70. return shapes
  71. .map(([namespace, shape]) => {
  72. let sum = 0;
  73. for (let i = 0; i < shapes.length; i++) {
  74. const targetShape = range[i % range.length];
  75. if (shape[i] === targetShape)
  76. sum++;
  77. }
  78. return [sum / shape.length, [namespace, shape]];
  79. })
  80. .sort((a, b) => b[0] - a[0])[0][1];
  81. }
  82. function inferItemMarker(options, { scales, library, coordinate, theme, markState }) {
  83. const shapeScale = (0, utils_1.scaleOf)(scales, 'shape');
  84. const [namespace, shapes] = inferShape(scales, markState);
  85. const { itemMarker } = options;
  86. if (shapeScale && !itemMarker) {
  87. return (d, i) => createShape(`${namespace}.${shapes[i]}`, library, coordinate, theme, {
  88. color: d.color,
  89. });
  90. }
  91. if (typeof itemMarker === 'function')
  92. return itemMarker;
  93. return (d, i) => createShape(itemMarker || `${namespace}.${shapes[i]}`, library, coordinate, theme, {
  94. color: d.color,
  95. });
  96. }
  97. function inferItemMarkerOpacity(scales) {
  98. const scale = (0, utils_1.scaleOf)(scales, 'opacity');
  99. if (scale) {
  100. const { range } = scale.getOptions();
  101. return (d, i) => range[i];
  102. }
  103. return undefined;
  104. }
  105. function inferItemMarkerSize(scales) {
  106. const scale = (0, utils_1.scaleOf)(scales, 'size');
  107. // only support constant size scale.
  108. // size in category legend means the marker radius.
  109. if (scale)
  110. return scale.map(NaN) * 2;
  111. return 8;
  112. }
  113. function inferCategoryStyle(options, context) {
  114. const { labelFormatter = (d) => `${d}` } = options;
  115. const { scales } = context;
  116. const baseStyle = {
  117. itemMarker: inferItemMarker(options, context),
  118. itemMarkerSize: inferItemMarkerSize(scales),
  119. itemMarkerOpacity: inferItemMarkerOpacity(scales),
  120. };
  121. const finalLabelFormatter = typeof labelFormatter === 'string'
  122. ? (0, d3_format_1.format)(labelFormatter)
  123. : labelFormatter;
  124. // here must exists a color scale
  125. const colorScale = (0, utils_1.scaleOf)(scales, 'color');
  126. const domain = (0, utils_1.domainOf)(scales);
  127. return Object.assign(Object.assign({}, baseStyle), { data: domain.map((d) => ({
  128. id: d,
  129. label: finalLabelFormatter(d),
  130. color: colorScale.map(d),
  131. })) });
  132. }
  133. function inferLegendShape(value, options, component) {
  134. const { position } = options;
  135. if (position === 'center') {
  136. const { bbox } = value;
  137. // to be comfirm: if position is center, we should use the width and height of user definition.
  138. const { width, height } = bbox;
  139. return { width, height };
  140. }
  141. const { width, height } = (0, utils_1.inferComponentShape)(value, options, component);
  142. return { width, height };
  143. }
  144. /**
  145. * Guide Component for ordinal color scale.
  146. */
  147. const LegendCategory = (options) => {
  148. const { labelFormatter, layout, order, orientation, position, size, title } = options, style = __rest(options, ["labelFormatter", "layout", "order", "orientation", "position", "size", "title"]);
  149. const { gridCol, gridRow } = style;
  150. return (context) => {
  151. var _a, _b, _c;
  152. const { value, theme } = context;
  153. const { bbox } = value;
  154. const { width, height } = inferLegendShape(value, options, exports.LegendCategory);
  155. const finalLayout = (0, utils_1.inferComponentLayout)(position, (_c = (_b = (_a = value.scales) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.guide) === null || _c === void 0 ? void 0 : _c.layout);
  156. const [finalGridRow, finalGridCol] = inferLayout(position, gridRow, gridCol);
  157. const legendStyle = Object.assign({ orientation: ['right', 'left', 'center'].includes(position)
  158. ? 'vertical'
  159. : 'horizontal', width,
  160. height, gridCol: gridCol !== null && gridCol !== void 0 ? gridCol : finalGridCol, gridRow: gridRow !== null && gridRow !== void 0 ? gridRow : finalGridRow, rowPadding: 0, colPadding: 8, titleText: (0, utils_1.titleContent)(title) }, inferCategoryStyle(options, context));
  161. const { legend: legendTheme = {} } = theme;
  162. const categoryStyle = (0, utils_1.adaptor)(Object.assign({}, legendTheme, legendStyle, style));
  163. const layoutWrapper = new utils_1.LegendCategoryLayout({
  164. style: Object.assign(Object.assign({ x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height }, finalLayout), {
  165. // @ts-ignore
  166. subOptions: categoryStyle }),
  167. });
  168. layoutWrapper.appendChild(new gui_1.Category({
  169. className: 'legend-category',
  170. style: categoryStyle,
  171. }));
  172. return layoutWrapper;
  173. };
  174. };
  175. exports.LegendCategory = LegendCategory;
  176. exports.LegendCategory.props = {
  177. defaultPosition: 'top',
  178. defaultOrder: 1,
  179. defaultSize: 40,
  180. };
  181. //# sourceMappingURL=legendCategory.js.map