legendHighlight.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.LegendHighlight = void 0;
  4. const d3_array_1 = require("d3-array");
  5. const helper_1 = require("../utils/helper");
  6. const utils_1 = require("./utils");
  7. const legendFilter_1 = require("./legendFilter");
  8. function LegendHighlight() {
  9. return (context, _, emitter) => {
  10. const { container, view, options } = context;
  11. const legends = (0, legendFilter_1.legendsOf)(container);
  12. const elements = (0, utils_1.selectG2Elements)(container);
  13. const channelOf = (legend) => {
  14. return (0, legendFilter_1.dataOf)(legend).scales[0].name;
  15. };
  16. const scaleOf = (channel) => {
  17. const { scale: { [channel]: scale }, } = view;
  18. return scale;
  19. };
  20. const markState = (0, utils_1.mergeState)(options, ['active', 'inactive']);
  21. const valueof = (0, utils_1.createValueof)(elements, (0, utils_1.createDatumof)(view));
  22. const destroys = [];
  23. // Bind events for each legend.
  24. for (const legend of legends) {
  25. const datumOf = (item) => {
  26. const { data } = legend.attributes;
  27. const { __data__: datum } = item;
  28. const { index } = datum;
  29. return data[index].label;
  30. };
  31. const channel = channelOf(legend);
  32. const items = (0, legendFilter_1.itemsOf)(legend);
  33. const scale = scaleOf(channel);
  34. const elementGroup = (0, d3_array_1.group)(elements, (d) => scale.invert(d.__data__[channel]));
  35. const { state: legendState = {} } = legend.attributes;
  36. const { inactive = {} } = legendState;
  37. const { setState, removeState } = (0, utils_1.useState)(markState, valueof);
  38. // Handle styles of inner item.
  39. const markerStyle = { inactive: (0, helper_1.subObject)(inactive, 'marker') };
  40. const labelStyle = { inactive: (0, helper_1.subObject)(inactive, 'label') };
  41. const { setState: setM, removeState: removeM } = (0, utils_1.useState)(markerStyle);
  42. const { setState: setL, removeState: removeL } = (0, utils_1.useState)(labelStyle);
  43. const updateLegendState = (highlight) => {
  44. for (const item of items) {
  45. const marker = (0, legendFilter_1.markerOf)(item);
  46. const label = (0, legendFilter_1.labelOf)(item);
  47. if (item === highlight || highlight === null) {
  48. removeM(marker, 'inactive');
  49. removeL(label, 'inactive');
  50. }
  51. else {
  52. setM(marker, 'inactive');
  53. setL(label, 'inactive');
  54. }
  55. }
  56. };
  57. const highlightItem = (event, item) => {
  58. // Update UI.
  59. const value = datumOf(item);
  60. const elementSet = new Set(elementGroup.get(value));
  61. for (const e of elements) {
  62. if (elementSet.has(e))
  63. setState(e, 'active');
  64. else
  65. setState(e, 'inactive');
  66. }
  67. updateLegendState(item);
  68. // Emit events.
  69. const { nativeEvent = true } = event;
  70. if (!nativeEvent)
  71. return;
  72. emitter.emit('legend:highlight', Object.assign(Object.assign({}, event), { nativeEvent, data: { channel, value } }));
  73. };
  74. const itemPointerover = new Map();
  75. // Add listener for the legend items.
  76. for (const item of items) {
  77. const pointerover = (event) => {
  78. highlightItem(event, item);
  79. };
  80. item.addEventListener('pointerover', pointerover);
  81. itemPointerover.set(item, pointerover);
  82. }
  83. // Add listener for the legend group.
  84. const pointerleave = (event) => {
  85. for (const e of elements)
  86. removeState(e, 'inactive', 'active');
  87. updateLegendState(null);
  88. // Emit events.
  89. const { nativeEvent = true } = event;
  90. if (!nativeEvent)
  91. return;
  92. emitter.emit('legend:unhighlight', { nativeEvent });
  93. };
  94. const onHighlight = (event) => {
  95. const { nativeEvent, data } = event;
  96. if (nativeEvent)
  97. return;
  98. const { channel: specifiedChannel, value } = data;
  99. if (specifiedChannel !== channel)
  100. return;
  101. const item = items.find((d) => datumOf(d) === value);
  102. if (!item)
  103. return;
  104. highlightItem({ nativeEvent: false }, item);
  105. };
  106. const onUnHighlight = (event) => {
  107. const { nativeEvent } = event;
  108. if (nativeEvent)
  109. return;
  110. pointerleave({ nativeEvent: false });
  111. };
  112. legend.addEventListener('pointerleave', pointerleave);
  113. emitter.on('legend:highlight', onHighlight);
  114. emitter.on('legend:unhighlight', onUnHighlight);
  115. const destroy = () => {
  116. legend.removeEventListener(pointerleave);
  117. emitter.off('legend:highlight', onHighlight);
  118. emitter.off('legend:unhighlight', onUnHighlight);
  119. for (const [item, pointerover] of itemPointerover) {
  120. item.removeEventListener(pointerover);
  121. }
  122. };
  123. destroys.push(destroy);
  124. }
  125. return () => destroys.forEach((d) => d());
  126. };
  127. }
  128. exports.LegendHighlight = LegendHighlight;
  129. //# sourceMappingURL=legendHighlight.js.map